Я новичок в Haskell, и я думаю, что понимаю монады и их механики (по крайней мере, для списка, штата, может быть, писателей и читателей), но я хочу понять, почему они были определены так, как они, или почему они должны быть такими, какие они есть, чтобы помочь моей интуиции в размышлении о них.
В частности, что такое чтение, которое делает монады-читатели или государственные монады необходимыми функциями (т.е. \s -> (a,s)
), а не только такими данными, как монада-писатель (т.е. (w,a)
)?
Кроме того, может ли монада писателя использоваться в качестве государственной монады, где журнал используется как строковое представление состояния, если функциональность MonadPlus
не используется? Является ли монадическая функция, используемая с монадами-писателями, разрешенной для просмотра текущего журнала и изменения, если это необходимо, или это только функция связывания монады-писателя, которая разрешена для просмотра журнала?
Кроме того, почему монады определены в терминах монадических функций, которые с типом a -> m b
, а не с типом m a -> mb
? Что так естественно в отношении функции, идущей от базового типа к монадированному типу?
Спасибо за ваши ответы.
Ответ 1
Государство и монада-читатели зависят от ценности того, что нас интересует.
Нам нужно иметь доступ к нему, иначе как мы могли бы написать что-то вроде
foo = do
i <- get
return $ if i == 1 then 2 else 3
Итак, наша государственная монада, естественно, выглядит как нечто, что принимает состояние, делает вещи и создает новый.
Аналогично с монадой-читателем мы берем какое-то состояние, магию и производим вывод (но не новое состояние, так как оно Reader s a <=> s -> a
.
Теперь это сильно отличается от монады-писателя. В писателе нас не волнует то, что было застряло в государстве ранее, все, что мы хотим сделать, это взять наше нынешнее состояние и зажечь еще кое-что. Поскольку наше состояние является моноидом, у нас есть гарантированное начальное состояние mempty
и способ застревания в mappend
. Это означает, что мы можем "запускать" каждое вычисление в пустом контексте, так сказать, и просто сливать выходы вместе и получать одинаковые результаты.
Хорошо, теперь есть несколько вопросов, поэтому я поеду один за раз
-
Если монада-писатель имела доступ к ранее написанным результатам, тогда она будет выглядеть как s -> (s, a)
, которая является государственной монадой:) Итак, что бы вы ни делали с государственной монадой, это можно сделать с помощью этой расширенный писатель.
На самом деле обе монады StateT
и WriterT
имеют экземпляры MonadPlus
, поэтому я не уверен, что вы здесь делаете.
-
Так как не существует произвольной функции m a -> a
, нам нужно каким-то образом развернуть вычисление для просмотра результатов ala >>=
, а так как return :: a -> m a
делает тривиальным идти другим путем, это более общий переход от простого к монадическому значению. В противном случае у нас просто были бы эти бесполезные IO String
, STM Int
и то, что мы не могли зависеть от остальных наших вычислений, так как fmap
не позволит нам поднимать больше побочных эффектов.
Ответ 2
Они определяются по-разному, потому что они делают разные вещи.
Возьмите монаду читателя. Начните с размышлений о том, что это значит, а не о том, как это работает.
Вычисление в монаде читателя - это то, которое зависит от дополнительной информации, читающей "среды". Таким образом, Reader Env Int
является Int
, который зависит от среды (типа Env
; если я оцениваю ее с помощью одной среды, я получаю одно значение Int
, и если я оцениваю ее с другой средой, Я получаю другое значение Int
. Если у меня нет среды, я не могу знать, какое значение имеет значение Reader Env Int
.
Теперь, какое значение даст мне Int
, если я дам ему Env
? Функция типа Env -> Int
! Таким образом, обобщение на e -> a
является монадой для каждого e
(с a
является параметром типа монады; (->) e
, если вам нравится префиксная нотация).
Теперь подумайте о значении монады-писателя. Вычисление в монаде писателя дает значение, но оно также дает дополнительное значение "сбоку": значение "log". И когда мы свяжем последовательность монадических вычислений из монады-писателя, значения журнала будут объединены (если мы хотим, чтобы тип журнала был моноидом, тогда это гарантирует, что значения журнала могут быть объединены без каких-либо других знаний о том, что они представляют). Таким образом, Writer Log Int
является Int
, который также имеет значение типа Log
.
Это очень похоже на пару: (Log, Int)
. И это обобщает на (w, a)
как монаду для каждого w
(с a
, являющимся параметром типа монады). Ограничение моноида на w
, которое гарантирует совмещение значений журнала, также означает, что мы имеем очевидное начальное значение (элемент идентичности для monoid: mempty
), поэтому нам не нужно ничего предоставлять, чтобы получить значение из значения в монаде писателя.
Модификация государственной монады s -> (a, s)
на самом деле в значительной степени является комбинацией вышеизложенного; a State S Int
является Int
, который зависит от значения S
(поскольку читатель зависит от среды), а также создает значение S
, где связывание последовательности вычислений состояний должно приводить к каждому из них "видя" состояние, произведенное предыдущим. Значение, зависящее от значения состояния, является функцией значения состояния; если на выходе появляется "вместе с" новым значением состояния, нам нужна пара.
Ответ 3
Кроме того, почему монады определены в терминах монадических функций, которые с типом a -> m b
, а не с типом m a -> m b
? Что так естественно в отношении функции, идущей от базового типа к монадированному типу?
(Я взял освобождение от добавления пробела между m
и b
в mb
).
Видите, это делает его монадой. Без этого у нас уже есть функции a -> b
и ссылка из них на f a -> f b
(эта ссылка называется "функтором" и подчиняется законам для fmap
). Но функтор только дает вам проекцию одного "мира" (категории) в другую - так что, какие бы законы ни находились в первом мире, они также сохраняются во втором мире (например, если a + b == c
, то f a (f +) f b == f c
). Монада дает вам мост между "мирами".
Кроме того, вам не нужно определять монаду с точки зрения поведения с функциями типа a -> m b
, но одна минимальная спецификация монады говорит вам, как >>=
, return
, id
и (.)
относиться. Можно определить монаду, используя >=>
, return
, id
и (.)
, или используя join
, return
, id
и (.)
- вы видите, это действительно не так вопрос, который функция выбрать. Оказывается, >>=
удобно для цепочки.
Ответ 4
В частности, что такое чтение, которое делает монады-читатели или государственные монады необходимыми функциями (т.е. \s -> (a,s)
), а не только такими данными, как монада-писатель (т.е. (w,a)
)?
Функции - это просто данные. Это просто те типы, которые лучше всего соответствуют желаемой семантике.
Кроме того, может ли монада-писатель использоваться в качестве государственной монады, где журнал используется как строковое представление состояния, если функция MonadPlus не используется?
Вы не могли передать непустое начальное состояние и не могли изменять его, кроме добавления значений. Если вы измените это, вы получите стандартную государственную монаду (кроме строк).
Является ли монадическая функция, используемая с монадами-писателями, разрешенной для просмотра текущего журнала и изменения, если это необходимо, или это только функция связывания монады-писателя, которая разрешена для просмотра журнала?
Вы заметите, что монада Writer имеет функцию tell
, которая фактически добавляет новые данные в журнал.
Кроме того, почему монады определены в терминах монадических функций, которые с типом a -> m b
, а не с типом m a -> m b
? Что так естественно в отношении функции, идущей от базового типа к монадированному типу?
Думаю, лучший ответ - "потому что a -> m b
оказывается более полезным".