Пусть против Лямбды в Haskell - PullRequest
4 голосов
/ 08 мая 2020

Я читаю «Начни программировать с Haskell» от Уилла Курта. В конце Урока 3, посвященного лексической области видимости, автор пишет:

Использование выражения let и лямбда-функции - это не совсем одно и то же. Например, следующий код вызовет ошибку, если вы попытаетесь его запустить:

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

Чтобы доказать, что let и лямбда не идентичны, перепишите функцию счетчика точно так, как здесь, но используйте вложенные лямбды вместо let.

Вот мое решение, которое работает так, как я ожидал:

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

Однако, как предлагает автор, если я запустил counter 2 в ghci, он зависнет навсегда (с использованием GH C 8.8.3).

Что происходит под капотом?

PS: Это работает, когда я правильно называю переменные.

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

1 Ответ

3 голосов
/ 08 мая 2020

И в лямбдах, и в let s каждый x затеняет предыдущий. Разница в размахе тени. Область действия лямбда-аргумента ограничена лямбда-выражением, но область действия привязки let - это вся структура let ... = ... in ..., таким образом, let x = x + 1 определяет x в терминах самого , а (\x -> x) (x + 1) тени x в пересчете на незатененные x. Позвольте мне продемонстрировать теневые области каждой из ваших реализаций, добавив число к каждой 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

Теперь должно быть ясно, почему они разные. В версии лямбда x1 назначается x0 + 1, а в версии let x1 назначается x1 + 1, который не завершается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...