Permalink
Browse files

Add a section on dealing with laziness

The advice in this section is what we actually do in many of our core
libraries and on the advice I've given to new Haskell programmers that
run into performance problem.
  • Loading branch information...
1 parent b96c29b commit 25eaf48ac1ad44fdfd6bc7b9dface414a8f705d5 @tibbe committed Jan 8, 2013
Showing with 79 additions and 9 deletions.
  1. +79 −9 haskell-style.md
View
@@ -70,9 +70,9 @@ Format records as follows:
```haskell
data Person = Person
- { firstName :: String -- ^ First name
- , lastName :: String -- ^ Last name
- , age :: Int -- ^ Age
+ { firstName :: !String -- ^ First name
+ , lastName :: !String -- ^ Last name
+ , age :: !Int -- ^ Age
} deriving (Eq, Show)
```
@@ -268,8 +268,8 @@ Record example:
```haskell
-- | Bla bla bla.
data Person = Person
- { age :: Int -- ^ Age
- , name :: String -- ^ First name
+ { age :: !Int -- ^ Age
+ , name :: !String -- ^ First name
}
```
@@ -279,11 +279,11 @@ For fields that require longer comments format them like so:
data Record = Record
{ -- | This is a very very very long comment that is split over
-- multiple lines.
- field1 :: Text
+ field1 :: !Text
-- | This is a second very very very long comment that is split
-- over multiple lines.
- , field2 :: Int
+ , field2 :: !Int
}
```
@@ -294,8 +294,8 @@ comments for data type definitions. Some examples:
```haskell
data Parser = Parser
- Int -- Current position
- ByteString -- Remaining input
+ !Int -- Current position
+ !ByteString -- Remaining input
foo :: Int -> Int
foo n = salt * 32 + 9
@@ -332,6 +332,76 @@ Use singular when naming modules e.g. use `Data.Map` and
`Data.ByteString.Internal` instead of `Data.Maps` and
`Data.ByteString.Internals`.
+Dealing with laziness
+---------------------
+
+By default, use strict data types and lazy functions.
+
+### Data types
+
+Constructor fields should be strict, unless there's an explicit reason
+to make them lazy. This avoids many common pitfalls caused by
+too much laziness and reduces the amount of brain cycles the
+programmer has to spend on thinking about evaluation order.
+
+```haskell
+-- Good
+data Point = Point
+ { pointX :: !Double -- ^ X coordinate
+ , pointY :: !Double -- ^ Y coordinate
+ }
+```
+
+```haskell
+-- Bad
+data Point = Point
+ { pointX :: Double -- ^ X coordinate
+ , pointY :: Double -- ^ Y coordinate
+ }
+```
+
+Additionally, unpacking simple fields often improves performance and
+reduces memory usage:
+
+```haskell
+data Point = Point
+ { pointX :: {-# UNPACK #-} !Double -- ^ X coordinate
+ , pointY :: {-# UNPACK #-} !Double -- ^ Y coordinate
+ }
+```
+
+As an alternative to the `UNPACK` pragma, you can put
+
+```haskell
+{-# OPTIONS_GHC -funbox-strict-fields #-}
+```
+
+at the top of the file. Including this flag in the file inself instead
+of e.g. in the Cabal file is preferable as the optimization will be
+applied even if someone compiles the file using other means (i.e. the
+optimization is attached to the source code it belongs to).
+
+Note that `-funbox-strict-fields` applies to all strict fields, not
+just small fields (e.g. `Double` or `Int`). If you're using GHC 7.4 or
+later you can use `NOUNPACK` to selectively opt-out for the unpacking
+enabled by `-funbox-strict-fields`.
+
+### Functions
+
+Have function arguments be lazy unless you explicitly need them to be
+strict.
+
+The most common case when you need strict function arguments is in
+recursion with an accumulator:
+
+```haskell
+mysum :: [Int] -> Int
+mysum = go 0
+ where
+ go !acc [] = acc
+ go acc (x:xs) = go (acc + x) xs
+```
+
Misc
----

0 comments on commit 25eaf48

Please sign in to comment.