Все мы любим do
, и мне было любопытно, если, возможно, этот вид альтернативного синтаксиса теоретически будет полезен за пределами мира монады. Если так, что другие виды вычислений это упростило бы? Имело бы смысл иметь что-то эквивалентное для Применимого, например?
В отношении самой нотации do
может быть полезно подумать, для чего она на самом деле хороша. Как указывает Трэвис Браун, я ранее выступал за использование стиля «функционального приложения» с Monad
s и родственными типами, но у этого есть и обратная сторона: Некоторые выражения просто могут Не должно быть написано чисто в стиле приложения прямых функций . Например, следующее может быстро сделать аппликативный стиль неуклюжим:
Написание такой функции как одного выражения обычно требует либо нескольких вложенных лямбда-выражений, либо абсурдной запутанной ерунды, которая придает бесточечному стилю плохую репутацию .С другой стороны, блок do
предоставляет синтаксический сахар для легкого вложенного определения промежуточных результатов со встроенным потоком управления.
Обычно вы, вероятно, извлекаете такие подвыражения и помещаете их в предложение where
или что-то в этом роде, но поскольку обычные значения образуют монаду с приложением функции как (>> =)
- - а именно монада Identity
- вы могли бы вместо этого написать такую функцию в блоке do
, хотя люди могут на вас посмеяться.
Помимо области видимости / связывания, блок do
делает за вас еще одну вещь - это исключение оператора, связывающего подвыражения вместе. Нетрудно представить себе другие случаи, когда было бы неплохо иметь обозначение для «объединить эти выражения, используя эту функцию в этом блоке», а затем позволить компилятору заполнить пробелы.
В простом случае, когда все выражения имеют один и тот же тип, размещение их в списке с последующим сворачиванием работает хорошо - построение строк таким образом с использованием неслов
и несогласованных строк
], например. Преимущество do
состоит в том, что он объединяет выражения с общей структурой и совместимыми, но не идентичными типами.
Фактически, тот же самый общий принцип верен для обозначения «скобок идиомы» из статьи Applicative
: там, где блоки do
используют символы новой строки для исключения монадической конструкции, скобки идиомы используют сопоставление чтобы исключить отмененное приложение функции.Нотация proc
для Arrow
также аналогична, и другие концепции также могут быть четко выражены таким образом, например:
Хотя это не слишком сложно превратить многие из них в один тип или полный экземпляр Monad
, было бы неплохо иметь унифицированный, расширяемый бит синтаксического сахара для общей концепции. Конечно, есть общая нить, связывающая все это и многое другое, но это гораздо более обширная тема, на самом деле не имеющая отношения к синтаксису ...
Мне кажется, что многие программисты Haskell вообще не любят do
, и это один из распространенных аргументов в пользу использования Applicative
], когда вам не нужна вся мощь Монады
, так это то, что комбинаторы <$>
, <*>
и т. д. позволяют очень ясно и кратко стиль кодирования.
Даже для монадического кода многие люди предпочитают явно использовать = <<
вместо нотации do
. Ответ camccann на ваш предыдущий вопрос о <*>
дает фантастический аргумент в пользу этого предпочтения.
Я обычно пишу свои первые черновики, используя do
, а затем заменяю их комбинаторами по мере исправления. Это всего лишь вопрос моего (не) опыта и вкуса: часто мне легче всего набрасывать вещи в более императивном стиле (что удобнее с do
), но я думаю, что не код
обычно красивее.
Для стрелок, с другой стороны, я не могу представить, чтобы не использовал proc
и команду do
. Кортежи так быстро становятся такими уродливыми.
Скобки идиомы - хороший способ думать о Applicatives, но они не единственное возможное такое расширение синтаксиса.
Филиппа Каудерой недавно отправила в haskell-cafe предложение о «Аппликативном do» нотации, отметив, что любая функция, которая выглядит примерно так:
foo = do
x <- bar
y <- baz
quux y 1234 x
где переменные связаны <-
встречается только в последней строке, может быть реализовано с Applicative - я фактически реализовал макрос на основе синтаксических правил для этого в схеме, которую я назвал 'ado'.
Это полезно в тех случаях, когда порядок аппликативных эффектов отличается от «естественного порядка» и предположение о существовании 'ado' в Haskell просто десахарирует:
foo = (\x y -> quux y 1234 x) <*> bar <*> baz
Однако правила лексической области видимости немного сбивают с толку .
В GHC есть препроцессор, который делает это для Arrows: http://www.haskell.org/ghc/docs/6.12.2/html/users_guide/arrow-notation.html
Нотация do
- это, по сути, способ сказать "преобразовать в лямбды по мере необходимости и
распределять >>=
между строками".
Когда очевидно, какой оператор используется для сворачивания всего, приятно опустить и использовать оператор "новой строки".
Программируемая новая строка была бы хорошим подходом к созданию списков, аппликативных цепочек и т.д. Для создания списков вам также понадобится "программируемый отступ". На самом деле, можно просто взять три значимых бита и сделать их все перегружаемыми:
do
. do
s. do
. Тогда, наверное, не стоит больше называть это do
. Возможно, это должна быть просто скобка.
Applicative
имеет (гораздо более ограниченные и более компактные) скобки-идиомы, смотрите Прикладное программирование с эффектами, страница 4. В среде Хаскеля Strathclyde Haskell Environment Конора МакБрайда, как мне кажется, реализованы такие скобки.
Я не вижу, как обобщить эти виды специальных синтаксисов, но, возможно, я недостаточно об этом задумывался.
BlazeHtml использует нотацию do
, хотя на самом деле это просто ] Моноид
(хотя и завернут в Монаду
, чтобы можно было использовать do
).
Таким образом, подобная запись для Monoid
s была бы здесь полезна.
Если вы посмотрите на код моей игры "Defend The King" , то я также много использую mconcat
ing, и, как и BlazeHtml, мне бы пригодился хороший поиск синтаксиса для этого.
Есть обобщение монад, которое подходит к do
нотации - параметризованные монады. Смотрите Beyond Monads от sigfpe. Пример использования:
test1' = do put 1
x <- get
put (show x)
y <- get
return (x,y)
Это "монада состояний", которая хранит сначала число, а затем строку.