Personal tools

Let vs. Where

From HaskellWiki

(Difference between revisions)
Jump to: navigation, search
(use 'select')
Line 54: Line 54:
 
</haskell>
 
</haskell>
   
In expression style, you might instead use a [[Case|functional equivalent]] of <hask>case</hask>
+
In expression style, you might use an explicit <hask>case</hask>:
in order to represent the guards:
+
  +
<haskell>
  +
f x
  +
= let a = w x
  +
in case () of
  +
_ | cond1 x = a
  +
| cond2 x = g a
  +
| otherwise = f (h x a)
  +
</haskell>
  +
  +
or a [[Case|functional equivalent]]:
  +
 
<haskell>
 
<haskell>
 
f x =
 
f x =
Line 64: Line 64:
 
</haskell>
 
</haskell>
   
Without such a function it looks worse. You would lose the guard structure,
+
or a series of if-then-else expressions:
and the heavier lexemes arguably make the resulting function harder to read:
 
   
 
<haskell>
 
<haskell>
Line 75: Line 75:
 
else f (h x a)
 
else f (h x a)
 
</haskell>
 
</haskell>
  +
  +
These alternatives are arguably less readable and hide the structure of the function more than simply using <hask>where</hask>.
   
 
== Lambda Lifting ==
 
== Lambda Lifting ==
Line 100: Line 102:
 
</haskell>
 
</haskell>
   
The auxilliary definition can either be a top-level binding, or included in f using let or where.
+
The auxilliary definition can either be a top-level binding, or included in f using <hask>let</hask> or <hask>where</hask>.
   
 
[[Category:Style]]
 
[[Category:Style]]

Revision as of 01:37, 15 November 2007

Haskell programmers often wonder, whether to use
let
or
where
.

This seems to be only a matter of taste in the sense of "Declaration vs. expression_style", however there is more about it.

It is important to know that
let ... in ...
is an expression,

that is, it can be written whereever expressions are allowed.

In contrast to that,
where
is bound to a surrounding syntactic construct,

like the pattern matching line of a function definition.

1 Advantages of let

Consider you have the function

f :: s -> (a,s)
f x = y
   where y = ... x ...
and later you decide to put this into the
Control.Monad.State
monad.

However, transforming to

f :: State s a
f = State $ \x -> y
   where y = ... x ...
will not work, because
where
refers to the pattern matching
 f =
, where no
x
is in scope. In contrast, if you had started with
let
, then you wouldn't have trouble.
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
In expression style, you might use an explicit
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)
These alternatives are arguably less readable and hide the structure of the function more than simply using
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)
The auxilliary definition can either be a top-level binding, or included in f using
let
or
where
.