Let vs. Where
From HaskellWiki
(Difference between revisions)
m (remove underscore) |
(Improved lay-out) |
||
| (3 intermediate revisions not shown.) | |||
| Line 1: | Line 1: | ||
Haskell programmers often wonder whether to use <hask>let</hask> or <hask>where</hask>. | Haskell programmers often wonder whether to use <hask>let</hask> or <hask>where</hask>. | ||
| - | This seems to be only a matter of taste in the sense of "[[Declaration vs. expression style]]", | + | This seems to be only a matter of taste in the sense of "[[Declaration vs. expression style]]", however there is more to it. |
| - | however there is more | + | |
| - | It is important to know that <hask>let ... in ...</hask> is an expression, | + | It is important to know that <hask>let ... in ...</hask> is an expression, that is, it can be written wherever expressions are allowed. In contrast, <hask>where</hask> is bound to a surrounding syntactic construct, like the [[pattern matching]] line of a function definition. |
| - | that is, it can be written wherever expressions are allowed. | + | |
| - | In contrast | + | |
| - | like the [[pattern matching]] line of a function definition. | + | |
== Advantages of let == | == Advantages of let == | ||
| - | + | Suppose you have the function | |
<haskell> | <haskell> | ||
f :: s -> (a,s) | f :: s -> (a,s) | ||
| Line 60: | Line 56: | ||
= let a = w x | = let a = w x | ||
in case () of | in case () of | ||
| - | _ | cond1 x | + | _ | cond1 x -> a |
| - | | cond2 x | + | | cond2 x -> g a |
| - | | otherwise | + | | otherwise -> f (h x a) |
</haskell> | </haskell> | ||
| Line 133: | Line 129: | ||
fib' n = fib (n - 1) + fib (n - 2) | fib' n = fib (n - 1) + fib (n - 2) | ||
</haskell> | </haskell> | ||
| - | you will notice, that the second one runs considerably slower than the first one. | + | you will notice, that the second one runs considerably slower than the first one. You may wonder, why just adding the explicit argument to <hask>fib</hask> (known as [[eta expansion]]) reduces the performance dramatically. |
| - | You may wonder, why just adding the explicit argument to <hask>fib</hask> (known as [[eta expansion]]) | + | |
| - | reduces the performance dramatically. | + | |
You might see the reason better, if you rewrite this code using <hask>let</hask>. | You might see the reason better, if you rewrite this code using <hask>let</hask>. | ||
| Line 154: | Line 148: | ||
in map fib' [0 ..] !! x | in map fib' [0 ..] !! x | ||
</haskell> | </haskell> | ||
| - | . In the second case <hask>fib'</hask> is (re-)defined for every argument <hask>x</hask>, | + | . In the second case <hask>fib'</hask> is (re-)defined for every argument <hask>x</hask>, thus it cannot be floated out.<br><br> |
| - | thus it cannot be floated out. | + | |
In contrast to that, in the first case <hask>fib'</hask> can be moved to the top level by a compiler. | In contrast to that, in the first case <hask>fib'</hask> can be moved to the top level by a compiler. | ||
The <hask>where</hask> clause hid this structure | The <hask>where</hask> clause hid this structure | ||
Current revision
Haskell programmers often wonder whether to uselet
where
This seems to be only a matter of taste in the sense of "Declaration vs. expression style", however there is more to it.
It is important to know thatlet ... in ...
where
Contents |
1 Advantages of let
Suppose you have the function
f :: s -> (a,s) f x = y where y = ... x ...
Control.Monad.State
However, transforming to
f :: State s a f = State $ \x -> y where y = ... x ...
where
f =
x
let
f :: s -> (a,s) f x = let y = ... x ... in y
This is easily transformed to:
f :: State s a f = State $ \x -> let y = ... x ... in y
2 Advantages of where
Because "where" blocks are bound to a syntactic construct, they can be used to share bindings between parts of a function that are not syntactically expressions. For example:
f x | cond1 x = a | cond2 x = g a | otherwise = f (h x a) where a = w x
case
f x = let a = w x in case () of _ | cond1 x -> a | cond2 x -> g a | otherwise -> f (h x a)
or a functional equivalent:
f x = let a = w x in select (f (h x a)) [(cond1 x, a), (cond2 x, g a)]
or a series of if-then-else expressions:
f x = let a = w x in if cond1 x then a else if cond2 x then g a else f (h x a)
where
3 Lambda Lifting
One other approach to consider is that let or where can often be implemented using lambda lifting and let floating, incurring at least the cost of introducing a new name. The above example:
f x | cond1 x = a | cond2 x = g a | otherwise = f (h x a) where a = w x
could be implemented as:
f x = f' (w x) x f' a x | cond1 x = a | cond2 x = g a | otherwise = f (h x a)
let
where
4 Problems with where
If you run both
fib = (map fib' [0 ..] !!) where fib' 0 = 0 fib' 1 = 1 fib' n = fib (n - 1) + fib (n - 2)
and
fib x = map fib' [0 ..] !! x where fib' 0 = 0 fib' 1 = 1 fib' n = fib (n - 1) + fib (n - 2)
fib
let
Compare
fib = let fib' 0 = 0 fib' 1 = 1 fib' n = fib (n - 1) + fib (n - 2) in (map fib' [0 ..] !!)
and
fib x = let fib' 0 = 0 fib' 1 = 1 fib' n = fib (n - 1) + fib (n - 2) in map fib' [0 ..] !! x
fib'
x
In contrast to that, in the first case
fib'
where
x
- Haskell-Cafe on Eta-expansion destroys memoization?
