Как каррирование работает с лямбда-выражениями в Haskell?

Просто посмотрите, как вы изучите Haskell (отличная работа) и в разделе каррированные функции есть пример, в котором говорится, что следующие две функции одинаковы:

addThree x y z = x + y + z  
addThree = \x -> \y -> \z -> x + y + z  

Что меня здесь смущает, так это то, как каррирование применяется к лямбда-выражениям. С типом функции

addThree :: (Num a) => a -> a -> a -> a  

Когда используются лямбды, функция равна \x -> (\y -> (\z -> x + y + z)) ? Если это так, то считается ли x + y константой в самой внутренней лямбде? То есть \z -> c + z где c это x + y ?


person mahonya    schedule 26.12.2013    source источник
comment
Вам следует начать с чтения о лямбда-исчислении.   -  person Hauleth    schedule 27.12.2013


Ответы (1)


При чтении сигнатур типов, таких как addThree :: Num a => a -> a -> a -> a, вы должны мысленно добавлять соответствующие скобки, поскольку стрелки правильно ассоциируют

addThree :: Num a => a ->  a ->  a -> a
addThree :: Num a => a -> (a -> (a -> a))

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

Говоря о лямбдах с карри, вы можете смотреть на вещи с нескольких сторон. Давайте рассмотрим этот внутренний фрагмент, например

\z -> x + y + z

Если бы это было все, что вам дали и попросили интерпретировать, вам пришлось бы в отчаянии всплеснуть руками — вообще нет определения того, что должны означать x и y. Они известны как «свободные» переменные, потому что они не ограничены лямбдой.

Чтобы дать им определение, мы должны обернуть больше лямбда-выражений, которые bind означают x и y.

\x -> \y ->     \z -> x + y + z       -- ok!

Итак, глядя изнутри наружу, выражение бессмысленно без этих внешних лямбда-выражений.

Однако, когда вы начнете вычислять выражение в приложении

(\x -> \y -> \z -> x + y + z) 1 2 3

тогда выходит другая история. Теперь вы должны обрабатывать каждую лямбда-привязку отдельно. Правило состоит в том, что для оценки лямбда-выражения, применяемого к значению, вы заменяете связанную переменную на это значение везде внутри него.

(\x -> \y -> \z -> x + y + z) 1 2
(      \y -> \z -> 1 + y + z)   2
(            \z -> 1 + 2 + z)    

Таким образом, извне выражение \z -> x + y + z никогда не существует, x и y исключаются, прежде чем мы дойдем до этого.

Однако стоит отметить, что это еще не совсем то же самое, что и c + z! Мы не оцениваем тело внутренней лямбды до привязки третьего аргумента. В каком-то смысле невозможно узнать, что находится внутри лямбды, пока мы не придадим ей какое-то значение; (\z -> 1 + 2 + z) полностью непрозрачен. Только после применения последнего аргумента мы можем начать вычислять добавление внутри тела.

В широком смысле это то, что известно как трудность работы «под связующим».

person J. Abrahamson    schedule 26.12.2013
comment
:i (+) ==› infixl 6 + ==› нет такой вещи, как \x y z -> x + y + z, только \x y z -> (x + y) + z, которую компилятор свободен/практически должен переписать/реализовать как \x y z -> let c=x+y in c + z... можно было разумно ожидать. - person Will Ness; 27.12.2013
comment
Согласен, хотя это больше связано с этапами оценки для (1 + 2) + 3, чем с семантикой лямбда. Если (\z -> (1 + 2) + z) никогда не применяется, то независимо от того, что происходит под лямбдой, это не имеет значения. - person J. Abrahamson; 27.12.2013
comment
Благодарю за разъяснение. Вопрос о c немного сложен. Я знаю, что это не оценивается, но когда оценивается самая внутренняя лямбда, c гарантированно будет 1 + 2. Будет ли здесь лучшим определением «связанные переменные» вместо константы? - person mahonya; 27.12.2013
comment
Да, c гарантированно будет (1 + 2) после выполнения приложения, но к тому времени, когда произойдет оценка, будет z := 3 (или какое-либо другое значение будет применено), так что больше нет разницы между (x + y) и z — все они были заменены конкретными значениями, поэтому оценка порядок теперь зависит от (+), а не от лямбды. Термин связанная переменная относится к разнице между x и z в \z -> x + zx свободен, потому что он должен определяться чем-то в окружающей среде, в то время как z связан, потому что он описан локально. - person J. Abrahamson; 27.12.2013