2

Learn You a Haskell presents the shortLines function:

shortLinesOnly :: String -> String  
shortLinesOnly input =   
    let allLines = lines input  
        shortLines = filter (\line -> length line < 10) allLines  
        result = unlines shortLines  
    in  result 

From this helpful post, it seems clear to me that the following let ... in is correct:

> (let x = 2 in x*2) + 3
7

But, in the above shortLinesOnly example, why is the let's in placed at in result?

Community
  • 1
  • 1
Kevin Meredith
  • 41,036
  • 63
  • 209
  • 384
  • Where else might it be placed? – Ganesh Sittampalam Jun 15 '14 at 20:54
  • 1
    @KevinMeredith This is a multi-line `let-in` statement. The `let-in` syntax lets you define one or more intermediate values that can be used in the expression following the `in`. This is like with `where` statements in which you can define as many local expressions as you want. There are some subtleties between let-in and where, namely that `let ... in ...` can be used in sub-expressions. You couldn't write the above as `(x * 2 where x = 2) + 3`, Haskell doesn't allow it. – bheklilr Jun 15 '14 at 21:03

3 Answers3

4

let ... in ... allows multiple declarations to come between the let and the in, and then a single expression after the in which is the final value of the entire let ... in ... expression. All the declarations are in scope for the expression (and for the body of each of the declarations).

So both the examples you give are valid - the simple one is just making a single declaration (x = 2) whereas the more complicated one is defining three things (allLines = ..., etc).

Ganesh Sittampalam
  • 28,821
  • 4
  • 79
  • 98
1

When you see let A in B you can think of B as the value of the statement. In an imperative language, you might write it as A; return B.

We could also have written the code as

shortLinesOnly :: String -> String  
shortLinesOnly input =   
    let allLines = lines input  
        shortLines = filter (\line -> length line < 10) allLines  
    in  unlines shortLines  

You can also think of let A in B as pretty much equivalent to B where A.

Thomas Ahle
  • 30,774
  • 21
  • 92
  • 114
1

If Haskell was a procedural language, you would write the function body like so:

allLines = lines input
shortLines = filter (\line -> length line < 10) allLines
return unlines shortLines

Converting this directly to the functional style would result in

let allLines = lines input
    shortLines = filter (\line -> length line < 10) allLines
in unlines shortLines

So, why bother with results? For better readability. The let statement usually stand for "define some parameters to be used in the main expression". But here unline shortLines is not the "main expression" of the function - it's just the final stage of the computation. To prevent it from taking the focus, another stage was added - namely result = unline shotLines. Now the "main expression" is result, which isn't really that interesting so it doesn't take the focus from the previous parts of the computation.

Idan Arye
  • 12,402
  • 5
  • 49
  • 68