Ослабить ограничения упорядочения в монадических вычислениях

вот некоторая пища для размышлений.

Когда я пишу монадический код, монада упорядочивает выполняемые операции. Например, если я напишу в монаде IO:

do a <- doSomething
   b <- doSomethingElse
   return (a + b)

Я знаю, что doSomething будет казнен раньше doSomethingElse.

Теперь рассмотрим эквивалентный код на таком языке, как C:

return (doSomething() + doSomethingElse());

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

Мой вопрос заключается в следующем: Как мне написать монадический код на Haskell, который также оставляет этот порядок вычисления неопределенным? В идеале я бы получил некоторые преимущества, когда оптимизатор моего компилятора просматривает код и начинает перемещать вещи.

Вот несколько возможных методов, которые не выполняют свою работу, но находятся в правильном «духе»:

  • Напишите код в функциональном стиле, то есть напишите plus doSomething doSomethingElse и позвольте plus планировать монадические вызовы. Недостатки: вы теряете совместное использование результатов монадических действий, и plus по-прежнему принимает решение о том, когда вещи в конечном итоге будут оценены.
  • Используйте отложенный ввод-вывод, то есть unsafeInterleaveIO, который откладывает планирование до требований отложенной оценки. Но lazy отличается от strict неопределенным порядком: в частности, я хочу, чтобы все мои монадические действия выполнялись.
  • Ленивый ввод-вывод в сочетании с немедленной последовательностью всех аргументов. В частности, seq не накладывает ограничений на порядок.

В этом смысле я хочу чего-то более гибкого, чем монадический порядок, но менее гибкого, чем полная лень.


person Edward Z. Yang    schedule 05.05.2011    source источник
comment
Почему он должен быть монадическим? И зачем вам навязывать неопределенный порядок?   -  person x13n    schedule 05.05.2011
comment
Это не так. Неопределенный порядок наиболее желателен с точки зрения оптимизации.   -  person Edward Z. Yang    schedule 05.05.2011
comment
Разве это примерно не то, что происходит, если вы используете ленивое состояние или монады ST?   -  person hammar    schedule 05.05.2011
comment
да. Но в IO у вас есть наблюдаемые эффекты, тогда как эффекты не наблюдаются вне State и ST.   -  person Edward Z. Yang    schedule 05.05.2011
comment
Итак, вам нужны эффекты, но без ограничений по порядку? Если да, то мне кажется, что вы не можете гарантировать детерминированность/правильность, не ограничивая типы эффектов, которые можно использовать.   -  person mightybyte    schedule 05.05.2011
comment
Итак, вам нужно что-то вроде unordered :: (IO a) -> (IO b) -> IO (a, b) с семантикой, что порядок побочных эффектов не определен. Похоже, вам нужен какой-то примитив, чтобы сделать это без накладных расходов forkIO.   -  person hammar    schedule 05.05.2011
comment
Это заставило меня кое о чем задуматься. Вы хотите принудительно некоторое упорядочить два, или это нормально, чтобы они потенциально происходили параллельно?   -  person hammar    schedule 05.05.2011
comment
Вы хотите ограничить себя коммутативными монадами, или вы хотите ввести недетерминизм?   -  person augustss    schedule 05.05.2011
comment
майтибайт: Ага. Не то чтобы вы могли гарантировать детерминизм/правильность в монаде IO... hammar: Кто знает? Я хочу, чтобы компилятор выбрал для меня! augustss: IO уже недетерминирован. Для других монад? Интересный вопрос!   -  person Edward Z. Yang    schedule 05.05.2011
comment
@Edward: Что, если две операции добавляют строку в файл, где вам не важен порядок строк в файле, но если они выполняются параллельно, вы можете получить странное чередование символов из два, поэтому в этом случае вам нужна семантика, гарантирующая некоторый порядок, тогда как, если вы знаете, что операции независимы, вы можете предоставить компилятору полную свободу.   -  person hammar    schedule 05.05.2011


Ответы (3)


Эта проблема чрезмерной последовательности монадного кода известна как "проблема коммутативных монад".

Коммутативные монады — это монады, для которых порядок действий не имеет значения (они коммутируют), то есть при следующем коде:

do a <- f x
   b <- g y
   m a b

такой же как:

do b <- g y
   a <- f x
   m a b

есть много коммутирующих монад (например, Maybe, Random). Если монада коммутативна, то захваченные в ней операции могут быть вычислены, например, параллельно. Они очень полезны!

Однако у нас нет хорошего синтаксиса для коммутирующих монад, хотя многие люди спрашивали о такой вещи - это все еще открытая исследовательская задача.

Кроме того, аппликативные функторы дают нам такую ​​свободу переупорядочивания вычислений, однако вы должны отказаться от понятия bind (как показывают предложения, например, для liftM2).

person Don Stewart    schedule 05.05.2011
comment
Maybe не кажется мне коммутативным: okay = do x <- Nothing ; y <- undefined ; return x против bad = do y <- undefined ; x <- Nothing ; return x. Возможно, это коммутирует в более ограничительном смысле? - person acfoltzer; 06.05.2011
comment
Они добираются до места назначения. эффекты, которые они описывают. При наличии исключений, незавершенности или других наблюдаемых эффектов, которые не отслеживаются монадой, вам не повезло. - person Don Stewart; 06.05.2011
comment
@DonStewart Ссылка на блог Томаса Петричека не работает. - person jub0bs; 21.07.2015

Это глубокий грязный хак, но, похоже, он должен помочь мне.

{-# OPTIONS_GHC -fglasgow-exts #-}
{-# LANGUAGE MagicHash #-}
module Unorder where
import GHC.Types

unorder :: IO a -> IO b -> IO (a, b)
unorder (IO f) (IO g) = IO $ \rw# ->
           let (# _, x #) = f rw#
               (# _, y #) = g rw#
           in (# rw# , (x,y) #)

Поскольку это передает недетерминизм в руки компилятора, он должен вести себя "правильно" (т.е. недетерминированно) и в отношении вопросов потока управления (т.е. исключений).

С другой стороны, мы не можем провернуть тот же трюк с большинством стандартных монад, таких как State и Either a, поскольку мы действительно полагаемся на жуткие действия на расстоянии, доступном при воздействии с токеном RealWorld. Чтобы получить правильное поведение, нам понадобилась бы некоторая аннотация, доступная оптимизатору, которая указывала бы, что мы согласны с недетерминированным выбором между двумя неэквивалентными альтернативами.

person sclv    schedule 05.05.2011
comment
Хех, это довольно фантастично. Теперь охарактеризуйте, когда мы можем безопасно использовать это ;-) - person Edward Z. Yang; 05.05.2011
comment
Я не собираюсь здесь претендовать на экспертные знания, но я думаю, что это достаточно безопасно и дает по существу желаемую семантику. Условия строгих кортежей должны обеспечивать гарантии упорядочения, которые нам нужны, в то время как структура предложения let должна избегать условий упорядочения, которых у нас нет. - person sclv; 05.05.2011
comment
Я не понимаю. Подробное объяснение того, что делает этот код, будет высоко оценено. - person user239558; 16.07.2011

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

Но что, если doSomething() вызовет побочный эффект, который изменит поведение doSomethingElse()? Вы действительно хотите, чтобы компилятор возился с порядком? (Подсказка: нет) Тот факт, что вы вообще находитесь в монаде, предполагает, что это может быть так. Ваше замечание о том, что «вы теряете обмен результатами», также предполагает это.

Однако обратите внимание, что монадический не всегда означает последовательный. Это не совсем то, что вы описываете, но вас может заинтересовать Par monad, которая позволяет выполнять ваши действия параллельно.

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

Примечание: не путайте return в Haskell с чем-то вроде return в C.

person Dan Burton    schedule 05.05.2011