Skip to content

Commit

Permalink
Add a section on dealing with laziness
Browse files Browse the repository at this point in the history
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
tibbe committed Jan 8, 2013
1 parent b96c29b commit 25eaf48
Showing 1 changed file with 79 additions and 9 deletions.
88 changes: 79 additions & 9 deletions haskell-style.md
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ Format records as follows:


```haskell ```haskell
data Person = Person data Person = Person
{ firstName :: String -- ^ First name { firstName :: !String -- ^ First name
, lastName :: String -- ^ Last name , lastName :: !String -- ^ Last name
, age :: Int -- ^ Age , age :: !Int -- ^ Age
} deriving (Eq, Show) } deriving (Eq, Show)
``` ```


Expand Down Expand Up @@ -268,8 +268,8 @@ Record example:
```haskell ```haskell
-- | Bla bla bla. -- | Bla bla bla.
data Person = Person data Person = Person
{ age :: Int -- ^ Age { age :: !Int -- ^ Age
, name :: String -- ^ First name , name :: !String -- ^ First name
} }
``` ```


Expand All @@ -279,11 +279,11 @@ For fields that require longer comments format them like so:
data Record = Record data Record = Record
{ -- | This is a very very very long comment that is split over { -- | This is a very very very long comment that is split over
-- multiple lines. -- multiple lines.
field1 :: Text field1 :: !Text


-- | This is a second very very very long comment that is split -- | This is a second very very very long comment that is split
-- over multiple lines. -- over multiple lines.
, field2 :: Int , field2 :: !Int
} }
``` ```


Expand All @@ -294,8 +294,8 @@ comments for data type definitions. Some examples:


```haskell ```haskell
data Parser = Parser data Parser = Parser
Int -- Current position !Int -- Current position
ByteString -- Remaining input !ByteString -- Remaining input


foo :: Int -> Int foo :: Int -> Int
foo n = salt * 32 + 9 foo n = salt * 32 + 9
Expand Down Expand Up @@ -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.Internal` instead of `Data.Maps` and
`Data.ByteString.Internals`. `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 Misc
---- ----


Expand Down

0 comments on commit 25eaf48

Please sign in to comment.