Пример глубокого понимания каррирования

Чтение https://wiki.haskell.org/Currying

Говорится :

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

Что такое техника/идиома Haskell, требующая более глубокого понимания каррирования?


person thepen    schedule 13.07.2016    source источник
comment
Аппликативные функторы — хороший пример того, как каррирование влияет на идиоматический язык Haskell. Метод <*> для Applicative определен в f (a -> b), функторе функции с одним аргументом, но поскольку все функции каррируются, все функции имеют один аргумент, поэтому функция <*> работает с функциями произвольной арности.   -  person Alexis King    schedule 14.07.2016
comment
Какой тип flip id? Что оно делает?   -  person melpomene    schedule 14.07.2016
comment
Возможно, для понимания таких безумных вещей, как printf, требуется глубочайшее понимание, но я бы не советовал вам сразу лезть в подобную сложность.   -  person dfeuer    schedule 14.07.2016
comment
Каррирование выглядит как тривиальное сокращение на уровне синтаксиса. Но на самом деле он позволяет перебирать произвольное количество аргументов функции. Вот как (например) QuickCheck может прозрачно тестировать N-арные функции.   -  person MathematicalOrchid    schedule 14.07.2016


Ответы (1)


Применение частичных функций на самом деле не является отличительной особенностью Haskell; это просто следствие каррированных функций.

map :: (a -> b) -> [a] -> [b]

В таком языке, как Python, map всегда принимает два аргумента: функцию типа a -> b и список типа [a].

map(f, [x, y, z]) == [f(x), f(y), f(z)]

Это требует, чтобы вы делали вид, что синтаксис -> предназначен только для галочки, и что -> между (a -> b) и [a] на самом деле не то же самое, что между [a] -> [b]. Однако это не так; это точно такой же оператор, и он правоассоциативен. Тип map может быть явно заключен в скобки как

map :: (a -> b) -> ([a] -> [b])

и внезапно кажется гораздо менее интересным, что вы можете указать только один аргумент (функцию) для map и получить в ответ новую функцию типа [a] -> [b]. Вот и все частичное применение функций: использование того факта, что все функции каррированы.

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

map f [1,2,3]

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

(map f) [1,2,3]

map сначала "частично" применяется к одному аргументу f, который возвращает новую функцию. Затем эта функция применяется к [1,2,3] для получения окончательного результата.

person chepner    schedule 14.07.2016
comment
Отличный ответ. Пример map, возможно, является тем случаем, когда на самом деле не нужно понимать, что происходит каррирование. Примером, когда приложение частичной функции действительно требует понимания каррирования, является, например. filter (== 5), где (==) :: Eq a => a -> a -> Bool, и так (==) 5 :: Eq a => a -> Bool, или sortBy (comparing f), где comparing :: Ord a => (b -> a) -> b -> b -> Ordering, и так comparing f :: b -> b -> Ordering, и так sortBy (comparing f) :: [b] -> [b]. - person Simon Shine; 14.07.2016