In Haskell, when do we use in with let?
haskell, let, scope, where-clause, whitespace
Solution
Short answer: Use `let` without `in` in the body of a do-block, and in the part after the `|` in a list comprehension. Anywhere else, use `let ... in ...`.
The keyword `let` is used in three ways in Haskell.
The first form is a let-expression.
let variable = expression in expression
This can be used wherever an expression is allowed, e.g.
> (let x = 2 in x*2) + 3
7
The second is a let-statement. This form is only used inside of do-notation, and does not use `in`.
do statements
let variable = expression
statements
The third is similar to number 2 and is used inside of list comprehensions. Again, no `in`.
> [(x, y) | x <- [1..3], let y = 2*x]
[(1,2),(2,4),(3,6)]
This form binds a variable which is in scope in subsequent generators and in the expression before the `|`.
The reason for your confusion here is that expressions (of the correct type) can be used as statements within a do-block, and `let .. in ..` is just an expression.
Because of the indentation rules of haskell, a line indented further than the previous one means it's a continuation of the previous line, so this
do let x = 42 in
foo
gets parsed as
do (let x = 42 in foo)
Without indentation, you get a parse error:
do (let x = 42 in)
foo
In conclusion, never use `in` in a list comprehension or a do-block. It is unneccesary and confusing, as those constructs already have their own form of `let`.
Problem
In the following code, the last phrase I can put an `in` in front. Will it change anything? Another question: If I decide to put `in` in front of the last phrase, do I need to indent it? I tried without indenting and hugs complains Last generator in do {...} must be an expression ``` import Data.Char groupsOf _ [] = [] groupsOf n xs = take n xs : groupsOf n ( tail xs ) problem_8 x = maximum . map product . groupsOf 5 $ x main = do t <- readFile "p8.log" let digits = map digitToInt $concat $ lines t print $ problem_8 digits ``` Edit Ok, so people don't seem to understand what I'm saying. Let me rephrase: are the following two the same, given the context above? 1. ``` let digits = map digitToInt $concat $ lines t print $ problem_8 digits ``` 2. ``` let digits = map digitToInt $concat $ lines t in print $ problem_8 digits ``` Another question concerning the scope of bindings declared in `let`: I read here that: `where` Clauses. Sometimes it is convenient to scope bindings over several guarded equations, which requires a where clause: ``` f x y | y>z = ... | y==z = ... | y<z = ... where z = x*x ``` Note that this cannot be done with a let expression, which only scopes over the expression which it encloses. My question: so, the variable digits shouldn't be visible to the last print phrase. Do I miss something here?