views:

128

answers:

4

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.

+13  A: 

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.

sepp2k
A: 

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.

Daniel Pratt
Sorry, this hasn't been asked.
Dario
What hasn't been asked?
Daniel Pratt
Rather I would say: it doesn't seem to address the question. Dave didn't seem to be asking about closures in general, and definitely not about closures in languages other than Haskell; he seemed to be asking how this was a properly constructed closure, since it didn't look to him like `x` was within the scope of the closure.
Owen S.
+11  A: 

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.

Dario
+1  A: 

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.

Yitz