В программировании на Haskell от Хаттона
In general, if
#is an operator, then expressions of the form(#),(x #), and(# y)for argumentsxandyare called sections, whose meaning as functions can be formalised using lambda expressions as follows:(#) = \x -> (\y -> x # y) (x #) = \y -> x # y (# y) = \x -> x # y
В чем разница и связь между «разделом» и «каррингом»?
Является ли раздел результатом применения операции каррирования к функции с несколькими аргументами?
Спасибо.
Представьте себе язык с функциями в инфиксной записи. Бинарная функция могла бы выглядеть так (2)sub(3) — да, мне тоже глаза режет. Левая/правая часть будет (2)sub/sub(3). Очевидно, что это не работает с функциями с несколькими аргументами. Единственная связь между каррированием и секциями, которую можно увидеть, заключается в том, что вам нужны каррированные функции для выражения с ними секций операторов.
В том смысле, что секции допускают частичное применение инфиксного оператора, аналогичного префиксной нотации, да.





Раздел — это просто специальный синтаксис для применения инфиксного оператора к одному аргументу. (# y) является более полезным из двух, поскольку (x #) эквивалентен (#) x (который просто применяет инфиксный оператор как функцию к одному аргументу обычным способом).
(#) это тоже раздел.
@melpomene Строго говоря, я так не думаю. Раздел 3.5 отчета говорит только о левых и правых разделах; синтаксис (#) упоминается отдельно в раздел 3.2.
Хм. Вы совершенно правы, но в книге Хаттона дается другое определение. И я смутно помню, что видел это где-то еще, только не могу вспомнить где.
@melpomene Вы напомнили мне об этом: раздел 4.2 История Haskell: лень с классом действительно относится к (#) как к разделу, а объединяющая тема заключается в том, чтобы сделать инфиксные операторы первоклассными. Можно сказать, что независимо от того, как в Отчете названы вещи, не случайно синтаксис в обоих случаях почти одинаков.
Однажды я спросил откуда пошло слово "секция", и там не было ясного источника. Меня не удивит отсутствие строгого определения; интуитивно я думаю об этом как о частичном применении инфиксных операторов, где (#) выглядит как разделы, но не обязательно связано с ними. Можно возразить, что оператор, «примененный» к нулевым аргументам, оценивает «основную» функцию.
Спасибо. В чем разница и связь между «разделом» и «каррингом»? Является ли раздел результатом применения операции каррирования к функции с несколькими аргументами?
Что-то вроде. Инфиксный оператор — это просто синтаксическая тонкость; она каррирована, как и любая другая функция, но вы пишете ее приложение в другой форме. Чтобы перетащить сюда обратные кавычки (где plus 3 5 и 3 `plus` 5 эквивалентны), разделы позволяют писать (3 +) так же, как вы можете писать plus 3. Вы также можете написать (+ 5) там, где нет прямого эквивалента, используя префиксную нотацию, не используя flip. (+ 5) то же, что flip plus 5.
@Tim Почему ты разместил этот комментарий? Это просто повторение вашего вопроса.
Левая и правая части — это синтаксические устройства для частичного применения инфиксного оператора к одному аргументу (см. также ответ Чепнера). Ради точности отметим, что каррирование — это не то же самое, что частичное применение:
Каррирование — это преобразование функции, которая принимает аргументы Н, в функцию, которая принимает один аргумент и возвращает функцию, которая принимает аргументы Н-1.
Частичное приложение создает функцию, которая принимает аргументы Н-1, из функции, которая принимает аргументы Н, предоставляя один из аргументов.
В Haskell бывает, что все каррировано; все функции принимают только один аргумент (даже некаррированные функции в Haskell принимают кортеж, который, строго говоря, является одним аргументом — вы можете поиграть с функциями curry и uncurry, чтобы увидеть, как это работает). Тем не менее, мы очень часто неформально думаем о функциях, возвращающих функции, как о функциях с несколькими аргументами. С этой точки зрения хорошим следствием каррирования по умолчанию является то, что частичное применение функции к ее первому аргументу становится тривиальным: в то время как, например, elem принимает значение и контейнер и проверяет, является ли значение элементом контейнера, elem "apple" берет контейнер (строк) и проверяет, является ли "apple" его элементом.
Что касается операторов, когда мы пишем, например...
5 / 2
... мы применяем оператор / к аргументам 5 и 2. Оператор также может использоваться в префиксной форме, а не в инфиксной:
(/) 5 2
В префиксной форме оператор может частично применяться обычным образом:
(/) 5
Это, однако, возможно, выглядит немного неловко — в конце концов, 5 здесь числитель, а не знаменатель. Я бы сказал, что в этом случае синтаксис левой секции проще для глаз:
(5 /)
Кроме того, частичное применение ко второму аргументу не так просто написать, требующее лямбда или flip. В случае с операторами в этом может помочь правая секция:
(/ 2)
Обратите внимание, что разделы также работают с функциями, преобразованными в операторы с помощью синтаксиса обратной кавычки, так что это...
(`elem` ["apple", "grape", "orange"])
... берет строку и проверяет, можно ли ее найти в ["apple", "grape", "orange"].
curry f x y = f (x,y). uncurry g (x,y) = g x y.
(+ 3) 4 = (+) 4 3 = 4 + 3. (4 +) 3 = (+) 4 3 = 4 + 3.
Раздел является результатом частичное применение функции карри: (+ 3) = flip (+) 3, (4 +) = (+) 4.
Каррированная функция (например, g или (+)) ожидает свои аргументы по одному. Функция без карри (например, f) ожидает свои аргументы в кортеже.
Чтобы частично применить функцию без каррирования, мы должны сначала превратить ее в функцию с каррированием с помощью curry. Чтобы частично применить каррированную функцию, нам ничего не нужно делать, просто примените ее к аргументу.
curry :: ((a, b) -> c ) -> ( a -> (b -> c))
uncurry :: (a -> (b -> c)) -> ((a, b) -> c )
x :: a
g :: a -> (b -> c)
--------------------
g x :: b -> c
x :: a
f :: (a, b) -> c
---------------------------
curry f :: a -> (b -> c)
curry f x :: b -> c
Раздел — это именно то, о чем говорится в цитируемом абзаце. Какое это имеет отношение к карри?