Loris
Loris

Reputation: 771

Let vs. Lambda in Haskell

I’m reading “Get programming with Haskell”, from Will Kurt. At the end of Lesson 3, which is about lexical scope, the author writes:

Using a let expression and a lambda function aren’t exactly the same thing under the hood. For example, the following code will cause an error if you try to run it:

counter x = let x = x + 1 in let x = x + 1 in x

To prove that let and lambda aren’t identical, rewrite the counter function exactly as it is here, but use nested lambdas instead of let.

Here’s my solution, which works as I expect it to do:

counterLambda x = (\x -> (\x -> x) (x + 1)) (x + 1)
-- counterLambda 2 == 4

However, as the author suggests, if I run counter 2 in ghci, it hangs forever (using GHC 8.8.3).

What’s happening under the hood?


PS: It works when I properly name the variables.

counter x = let a = x + 1 in let b = a + 1 in b
-- counter 2 == 4

Upvotes: 3

Views: 1019

Answers (1)

In both the lambdas and the lets, each x shadows the one before. The difference is in the scope of the shadow. The scope of a lambda argument is limited to the lambda, but the scope of a let binding is the entire let ... = ... in ... structure, thus let x = x + 1 defines x in terms of itself, while (\x -> x) (x + 1) shadows x in terms of the unshadowed x. Let me demonstrate the shadow scopes of each of your implementations by adding a number to each x:

counterLambda x0 = (\x1 -> (\x2 -> (\x3 -> x3) x2) (x1 + 1)) (x0 + 1)

counter       x0 = let x1 = x1 + 1 in let x2 = x2 + 1 in x2

Now it should be clear why these are different. In the lambda version, x1 is assigned x0 + 1, while in the let version, x1 is assigned x1 + 1, which does not terminate.

Upvotes: 3

Related Questions