Подтвердить что ты не робот

Хаскелл: Вычисление "в монаде" - что означает?

Прочитав о монадах, я продолжаю видеть фразы типа "вычисления в монаде Xyz". Что значит для вычисления "в" определенной монаде?

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

Но я не понимаю, как вычисление будет считаться "в" монаде. Это относится только к функции, которая производит монадический результат?

Примеры: (поиск "вычисление в" )

4b9b3361

Ответ 1

Как правило, "вычисление в монаде" означает не только функцию, возвращающую монадический результат, но и такую ​​функцию, которая используется внутри блока do или как часть второго аргумента для (>>=), или что-либо еще эквивалентное тем. Различие имеет отношение к тому, что вы сказали в комментарии:

"Вычисление" происходит в func f, после того, как val извлечен из входной монады, и до того, как результат будет завернут как монада. Я не вижу, как само по себе вычисление "в" монаде; это кажется явно "вне" монады.

Это не плохой способ думать об этом - на самом деле, нотация do поощряет это, потому что это удобный способ взглянуть на вещи - но это приводит к слегка вводящей в заблуждение интуиции. Нигде ничего не "извлекают" из монады. Чтобы понять, почему, забудьте о (>>=) - это составная операция, которая существует для поддержки нотации do. Более фундаментальное определение монады - три ортогональные функции:

fmap :: (a -> b) -> (m a -> m b)
return :: a -> m a
join :: m (m a) -> m a

... где m - монада.

Теперь подумайте о том, как реализовать (>>=) с ними: начиная с аргументов типа m a и a -> m b, ваш единственный вариант использует fmap, чтобы получить что-то типа m (m b), после чего вы можете использовать join, чтобы сгладить вложенные "слои", чтобы получить только m b.

Другими словами, ничто не берется "из" монады - вместо этого подумайте о том, что вычисления будут идти глубже в монаду, а последующие шаги будут свернуты в один слой монады.

Обратите внимание, что законы монады также намного проще с этой точки зрения - по сути, они говорят, что при применении join не имеет значения до тех пор, пока сохраняется порядок вложения (форма ассоциативности) и что монадический слой, введенный return, ничего не делает (значение идентичности для join).

Ответ 2

Означает ли это только функцию, которая производит монадический результат?

Да, короче.


В долгосрочной перспективе это потому, что Monad позволяет вводить в него значения (через return), но один раз внутри Monad они застревают. Вы должны использовать некоторую функцию, например evalWriter или runCont, которая является более специфичной, чем Monad, чтобы вернуть значения "out".

Более того, Monad (на самом деле, его партнер, Applicative) является сутью наличия "контейнера" и разрешения вычислений внутри него. То, что дает (>>=), возможность делать интересные вычисления "внутри" Monad.

Таким образом, функции, такие как Monad m => m a -> (a -> m b) -> m b, позволяют вычислять с и вокруг и внутри Monad. Такие функции, как Monad m => a -> m a, позволяют вводить в Monad. Такие функции, как m a -> a, позволят вам "избежать" Monad, за исключением того, что они вообще не существуют (только в определенных). Итак, для беседы нам нравится говорить о функциях, которые имеют такие типы результатов, как Monad m => m a как "внутри монады".

Ответ 3

Обычно материал монады легче понять, когда начинаешься с "сборных" монад в качестве примера. Представьте, что вы вычисляете расстояние двух точек:

data Point = Point Double Double

distance :: Point -> Point -> Double
distance p1 p2 = undefined

Теперь у вас может быть определенный контекст. Например. одна из точек может быть "незаконной", потому что она выходит за рамки (например, на экране). Итак, вы завершаете существующее вычисление в монаде Maybe:

distance :: Maybe Point -> Maybe Point -> Maybe Double
distance p1 p2 = undefined

У вас точно такое же вычисление, но с дополнительной функцией, которая может быть "без результата" (закодирована как Nothing).

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

distance :: [Point] -> [Point] -> [Double]
distance p1 p2 = undefined

Или точки вводятся пользователем, что делает расчет "недетерминированным" (в том смысле, что вы зависите от вещей во внешнем мире, которые могут измениться), тогда монада IO является вашим другом:

distance :: IO Point -> IO Point -> IO Double
distance p1 p2 = undefined

Вычисление остается неизменным, но происходит в определенном "контексте", что добавляет некоторые полезные аспекты (сбой, многозначность, недетерминизм). Вы даже можете объединить эти контексты (монадные трансформаторы).

Вы можете написать определение, которое унифицирует приведенные выше определения и работает для любой монады:

 distance :: Monad m => m Point -> m Point -> m Double
 distance p1 p2 = do
     Point x1 y1 <- p1
     Point x2 y2 <- p2
     return $ sqrt ((x1-x2)^2 + (y1-y2)^2)  

Это доказывает, что наш расчет действительно независим от фактической монады, что приводит к формулировкам, поскольку "x вычисляется в (-side) y-монаде".

Ответ 4

Посмотрев на ссылки, которые вы предоставили, кажется, что общее использование "вычислений в" относится к одному монадическому значению. Выдержки:

Нежное введение - здесь мы запускаем вычисление в монаде SM, но вычисление является монадическим значением:

-- run a computation in the SM monad
runSM                   :: S -> SM a -> (a,S)

Все о монадах - предыдущее вычисление относится к монадическому значению в последовательности:

Функция → - это удобный оператор, который используется для привязки монадического вычисления, которое не требует ввода из предыдущего вычисления в последовательности

Понимание монадов - здесь первое вычисление может относиться к примеру. getLine, монадическое значение:

(binding) дает внутреннюю идею использования результата вычисления в другом вычислении, не требуя понятия текущих вычислений.

Итак, в качестве аналогии, если я говорю i = 4 + 2, тогда i - это значение 6, но оно равно вычисляется, а именно вычисление 4 + 2. Кажется, что связанные страницы используют вычисление в этом смысле - вычисление как монадическое значение - по крайней мере, некоторое время, и в этом случае имеет смысл использовать выражение "вычисление в" данной монаде.

Ответ 5

Рассмотрим монаду IO. Значение типа IO a представляет собой описание большого (часто бесконечного) числа поведения, где поведение представляет собой последовательность событий IO (чтение, запись и т.д.). Такое значение называется "вычислением"; в этом случае это вычисление в монаде IO.