Идиомы композиции функций (.) И приложения функции ($) в Haskell: правильное использование

129

Я читал Real World Haskell , и я приближался к концу, но вопрос стиля был мелочным у меня делать с (.)и ($)операторами.

Когда вы пишете функцию, которая является композицией других функций, вы пишете ее так:

f = g . h

Но когда вы применяете что-то в конце этих функций, я пишу это так:

k = a $ b $ c $ value

Но книга написала бы это так:

k = a . b . c $ value

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

Есть ли причина использовать книжный способ, который намного лучше, чем использование всех ($)символов? Или здесь есть какая-то передовая практика, которую я не понимаю? Или это лишнее, и я не должен об этом вообще беспокоиться?

Роберт Массайоли
источник
4
Обратите внимание, что второй пример можно сделать какk = a $ b $ c value
Thomas Eding
1
Да, может, как упомянул Зифре ниже, но я решил оставить это там, так как это ничему не вредит и делает его (а теперь и ваши) комментарии осмысленными. Однако, спасибо. +1 :)
Роберт Массайоли
2
Другой распространенный подход - a . b $ c valueкоторый мне не нравится так сильно, как третий пример, но он сохраняет несколько символов.
John L

Ответы:

153

Думаю, я могу ответить на этот вопрос авторитетно.

Есть ли причина использовать книжный способ, который намного лучше, чем использование всех символов ($)?

Особой причины нет. Мы с Брайаном предпочитаем уменьшать линейный шум. .тише чем $. В результате в книге используется f . g . h $ xсинтаксис.

Дон Стюарт
источник
3
Что ж, я не могу надеяться на лучший ответ, чем этот; от одного из авторов. :) И в этом есть смысл, когда я читаю, на странице он выглядит намного тише. Отметьте это как ответ, потому что он напрямую отвечает на вопрос. Спасибо за ответ; и, по сути, книга.
Роберт Массайоли
38
Не могу не согласиться с автором, но я думаю, что есть еще одна более важная причина в ментальной модели создания чего-либо, а не использования этого. Пользователи Haskell склонны f.g.hскорее думать о нем как о новом умном творении f(g(h())). Теперь они вызывают новую, хотя и анонимную, функцию, которую они создали, а не просто связывают большой словарь заранее подготовленных вызовов функций, как пользователь PHP.
Эван Кэрролл,
1
Это интересное утверждение: «уменьшить линейный шум» и «тише, чем». Я никогда не думал о языках программирования в этой терминологии, но это имеет смысл.
Rabarberski
53

Они действительно эквивалентны: имейте в виду, что $оператор, по сути, ничего не делает. f $ xоценивается в f x. Целью $является его фиксированное поведение: правоассоциативный и минимальный приоритет. После удаления $и использования скобок для группировки вместо приоритета инфиксов фрагменты кода выглядят следующим образом:

k = a (b (c (value)))

и

k = (a . b . c) value

Причина, по которой .версия предпочтительнее $версии, та же, что и для обеих версий, заключенных в скобки выше: эстетическая привлекательность.

Хотя некоторые могут задаться вопросом, основано ли использование инфиксных операторов вместо круглых скобок на каком-то подсознательном побуждении избежать любого возможного сходства с Лиспом (шутка ... я думаю?).

CA McCann
источник
38

Я хотел бы добавить , что в f . g $ x, f . gявляется значимой синтаксической единицей.

Между тем, in f $ g $ x, f $ gне имеет смысла. $Возможно, более важна цепочка - первый получить результат gиз x, а затем сделать fего, а затем сделать fooего, то и т.д.

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

sclv
источник
2
Я изучаю Haskell (ничего значимого не закодировал), но мне нравится думать о $ как о чем-то вроде оператора «трубы». Это очень похоже на терминал | pipe (за исключением потоков данных справа налево), но, как и в терминальных процессах, каждая единица явно независима.
LostSalad
1
$Оператор , кажется, ведут себя аналогично <|оператору в F #. На самом деле, я считаю, что оба они определены одинаково - в F #,(<|) a b = a b и Haskell a $ b = a b, которые по сути эквивалентны.
Tom Galvin
@Quackmatic Вполне уверен, что это (<|) b a = a bна F #, так как он используется как [1; 2; 3; 4; 5] <| List.map ((+) 1), если память не изменяет.
BalinKingOfMoria Reinstate CMs
@BalinKingOfMoria <|Оператор передает значение функции, а не передает функцию в значение - |>вместо этого ваш пример кода будет работать с оператором.
Том Гэлвин
@Quackmatic Ой. Не знал, что существует несколько версий (я не очень много использовал F #).
BalinKingOfMoria Reinstate CMs
18

Для меня, я думаю, ответ: (а) аккуратность, как сказал Дон ; и (б) я обнаружил, что когда я редактирую код, моя функция может закончиться безточечным стилем, и тогда все, что мне нужно сделать, это удалить последнюю, $вместо того, чтобы возвращаться и менять все. Мелочь, конечно, но тонкость.

Антал Спектор-Забуски
источник
Да, это хорошая причина написать это так, если вы хотите закончить композицию функций, тогда это будет быстрее. :) Ура за экономию нажатий клавиш, но главное время. +1
Роберт Массайоли
13

Есть интересное обсуждение этого вопроса на ветке haskell-cafe . По- видимому , есть меньшинство точка зрения , которая считает , что право ассоциативности $является «просто неправильно» , и выбор f . g . h $ xболее чем f $ g $ h $ xодин из способов боковых шагового вопроса.

Трэвис Браун
источник
Эй, вы нашли целую дискуссию по этому поводу. Это здорово, сейчас читаю. +1
Роберт Массайоли
2
Подобные аргументы и здесь: ghc.haskell.org/trac/haskell-prime/wiki/…
enrique
Обратите внимание, что у $!него также есть «совершенно неправильная» правильная ассоциативность - почему $! оператор правоассоциативный? ,
imz - Иван Захарящев 07
3

Это просто вопрос стиля. Однако то, как это написано в книге, имеет для меня больше смысла. Он объединяет все функции, а затем применяет их к значению.

Ваш метод просто выглядит странно, а последний $не нужен.

Однако на самом деле это не имеет значения. В Haskell обычно есть много, много правильных способов сделать то же самое.

Zifre
источник
3
Я не заметил, что последний доллар был ненужным, но должен был. Спасибо, и я оставлю это там, чтобы люди знали, что означает этот комментарий.
Роберт Массайоли
0

Я понимаю, что это очень старый вопрос, но я думаю, что есть еще одна причина, о которой не упоминалось.

Если вы объявляете новую функцию без точек f . g . h, значение, которое вы передаете, будет автоматически применено. Однако если вы напишете f $ g $ h, это не сработает.

Я думаю, что причина, по которой автор предпочитает метод композиции, заключается в том, что он приводит к хорошей практике построения функций.

hackeryarn
источник