Как это позволяет выражать работу?

Учитывая эту строку кода Haskell, моя задача состояла в том, чтобы оценить ее в самой простой форме.

let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20

Мне уже дали ответ (и, конечно же, он сам оценил его в GHCI): 42

Однако я хотел бы получить лучшее представление о том, как эта оценка действительно работает здесь. В общем, я думаю, что знаю, как (простые) выражения работают:

Пример

a = let y = 5 in y * 5  -- a == 25

Это оценивается как 25, потому что мы привязываем y к значению 5 и a присваивается значение y*5 (часть после in). Связывание y = 5 допустимо только в пределах области let.

Пока единственная интерпретация (которая, по меньшей мере, оценивается до 42), такова:

let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20
  • g (\x -> k (h x))
  • h - (+1) (функция (\x -> x+1))
  • k (\x -> x+x)

    • 20 - это вход g, который дает k (h 20)
    • h 20 дает 20 + 1= 21
    • k (h 20)= k 21= 21 + 21= 42

Но меня смущает использование g h k после let. Что это значит?

Ответ 1

Подумайте о определении функции. Если вы пишете:

g h k x = k (h x)

Затем это функция, которая принимает три параметра h, k и x и возвращает k (h x). Это эквивалентно:

g h k = \x -> k (h x)

или

g h = \k x -> k (h x)

или

g = \h k x -> k (h x)

Таким образом, мы можем передавать переменные между головкой функции и лямбда-выражением в теле. На самом деле компилятор Haskell перепишет его.

Итак, с выражением let мы определяем функцию локального охвата, подобную той, которая определена выше. Теперь, если мы назовем g (+1) (\x -> x+x) 20, тогда вызываем g с помощью h = (+1), k = (\x -> x+x) и x = 20.

Итак, мы будем оценивать его как:

(\x -> x+x) ((+1) 20)

который оценивается как:

   (\x -> x+x) ((+1) 20)
-> ((+1) 20)+((+1) 20)
-> 21 + 21
-> 42

Ответ 2

g h k = ... - определение функции. Это означает, что результат применения g к двум аргументам (с именем h и k) будет оцениваться частью .... Другими словами, это ярлык для g = \h -> \k -> ....

Таким образом, мы можем упростить выражение шаг за шагом следующим образом:

let g h k = (\x -> k (h x)) in g (+1) (\x -> x+x) 20
let g = \h -> \k -> (\x -> k (h x)) in g (+1) (\x -> x+x) 20
(\h -> \k -> (\x -> k (h x))) (+1) (\x -> x+x) 20
(\k -> (\x -> k ((+1) x))) (\x -> x+x) 20
(\x -> (\x -> x+x) ((+1) x)) 20
(\x -> x+x) ((+1) 20)
(\x -> x+x) 21
21 + 21
42