davetapley
davetapley

Reputation: 17968

Haskell: Scope of variable when using lambda expression with bind functions

The following line works as expected, but I am a little concerned why:

getLine >>= \x-> getLine >>= \y-> return [x, y]

Consider the addition of parenthesis to scope the lambda expressions:

getLine >>= (\x-> getLine) >>= (\y-> return [x, y])

The second line is errorneous because x is not in scope when used in the return, and I am happy with this. What concerns me this that in the first line the scope of x appears to have 'leaked' out.

Is this 'leaking' considered bad practice? I am confused as to how it has remained in scope and not been lost immediately after the \x -> getLine expression.

Upvotes: 4

Views: 1635

Answers (4)

Yitz
Yitz

Reputation: 5117

Haskell syntax follows the "maximal munch" rule: each syntax elements extends as far as it possibly can until extending it any further would result in an error. Since it works fine to extend the scope of the "\x ->" all the way to the end, that's what it does.

Upvotes: 2

Dario
Dario

Reputation: 49218

You parentheses are simply set wrong. The correct parentheses are

getLine >>= (\x -> getLine >>= (\y -> return [x, y]))

And therefore, x is of course defined in the body.

Note that this feature is particularly useful since you can format code like this:

getLine >>= \x ->
getLine >>= \y ->
return [x, y]

which is almost

do
  x <- getLine
  y <- getLine
  return [x, y]

but without any special syntax.

Upvotes: 12

Daniel Pratt
Daniel Pratt

Reputation: 12077

The reference to x in the return expression is known as a closure. It's a typical feature of functional programming languages. In a pure functional programming language, like Haskell, there is no cause for concern. It's a very nice feature. In 'hybrid' functional programming languages like F# (and C# these days), closures can indeed result in unexpected behavior sometimes.

Upvotes: 1

sepp2k
sepp2k

Reputation: 370367

If you parenthesize your first line correctly according to haskell's precedence rules, you get:

getLine >>= (\x-> getLine >>= (\y-> return [x, y]))

This makes it obvious that the second lambda is inside the first, so there's no problem with it accessing the variables of the first lambda.

Upvotes: 13

Related Questions