До сих пор каждая монада (которая может быть представлена как тип данных), с которой я столкнулась, имела соответствующий трансформатор монады или могла иметь ее. Есть ли такая монада, которая не может иметь ее? Или все монады имеют соответствующий трансформатор?
Я хотел бы увидеть либо доказательство (в идеале конструктивное), которое имеет каждая монада, либо пример конкретной монады, у которой ее нет (с доказательством). Меня интересуют как более ориентированные на Haskell ответы, так и теоретические (категории).
Ответ 3
Обновление: утверждения 1 и 2 ниже, скорее всего, неверны; Я думаю, что нашел монадные трансформаторы для этих случаев. Я еще не закончил необходимые расчеты, но они выглядят многообещающими. Заявления 3 и 4 остаются в силе. Я добавил утверждение 5, чтобы показать пример монады с двумя различными трансформаторами.
Преобразователь для Either a (z → a)
(наиболее вероятно) n (Either a (z → ma)
, где m
- произвольная инородная монада. Преобразователь для (a → np) → na
- (наиболее вероятно) (a → tmp) → tma
где tm
- преобразователь для монады n
.
- Я думаю, что нашел по крайней мере один контрпример: простую и явную монаду, которая не имеет простого и явного преобразователя монад.
Конструктор типа монады L
для этого контрпримера определяется как
type L z a = Either a (z -> a)
Цель этой монады - украсить обычную читательскую монаду z → a
явным pure
значением (Left x
). Обычное читаемое монадное pure
значение - это постоянная функция pure x = _ → x
. Однако, если нам дано значение типа z → a
, мы не сможем определить, является ли это значение постоянной функцией. При L za
pure
значение явно представляется как Left x
. Теперь пользователи могут сопоставлять с образцом L za
и определять, является ли данное монадическое значение чистым или имеет эффект. Кроме этого, монада L z
делает то же самое, что и монада читателя.
Экземпляр монады:
instance Monad (L z) where
return x = Left x
(Left x) >>= f = f x
(Right q) >>= f = Right(join merged) where
join :: (z -> z -> r) -> z -> r
join f x = f x x -- the standard 'join' for Reader monad
merged :: z -> z -> r
merged = merge . f . q -- 'f . q' is the 'fmap' of the Reader monad
merge :: Either a (z -> a) -> z -> a
merge (Left x) _ = x
merge (Right p) z = p z
Эта монада L z
является частным случаем более общей конструкции, (Monad m) => Monad (L m)
где L ma = Either a (ma)
. Эта конструкция украшает заданную монаду m
, добавляя явное pure
значение (Left x
), так что теперь пользователи могут сопоставлять шаблоны с L m
чтобы решить, является ли значение чистым. Во всех других отношениях L m
представляет тот же вычислительный эффект, что и монада m
.
Экземпляр монады для L m
почти такой же, как и в примере выше, за исключением того, что необходимо использовать join
и fmap
монады m
, а merge
вспомогательной функции определяется как
merge :: Either a (m a) -> m a
merge (Left x) = return @m x
merge (Right p) = p
Я проверил, что законы монады справедливы для L m
с произвольной монадой m
.
Итак, я думаю, что L m
не имеет монадного преобразователя, ни для общего m
ни даже для простой монады m = Reader
. Достаточно рассмотреть L z
как определено выше; даже эта простая монада, похоже, не имеет трансформатора.
(Эвристическая) причина несуществования монадного трансформатора заключается в том, что у этой монады есть Reader
внутри Either
. Either
монадный преобразователь нуждается в том, чтобы его базовая монада была составлена внутри внешней монады, EitherT ema = m (Either ea)
, потому что монадный преобразователь работает с использованием обхода. Похоже, что любая монада, содержащая Either
в своем типе данных, нуждается в обходе для работы монадного преобразователя, и поэтому в преобразователе должна быть "внутренняя" композиция. Однако для преобразования монад Reader
его базовая монада должна быть составлена вне внешней монады, ReaderT rma = r → ma
. Монада L
представляет собой композицию типа данных, которая требует составного внутреннего преобразователя, и монаду, которая требует составного внешнего преобразователя, а вторая монада находится внутри первого, что невозможно согласовать. Независимо от того, как мы пытаемся определить L-трансформатор LT
, кажется, что мы не можем удовлетворить законы монадных трансформаторов.
Одной из возможностей определения конструктора типа LT
будет LT zma = Either a (z → ma)
. Результатом является законная монада, но морфизм ma → LT zma
не сохраняет m
return
потому что return x
отображается в Right (\z → return x)
, который не является return
L
(всегда Left x
).
Другой возможностью является LT zma = z → Either a (ma)
. В результате получается монада, но снова m
return
отображается в \_ → Right (...)
вместо Left (...)
как требуется для монады z → Either a (ma)
.
Еще одна возможность объединения доступных конструкторов типов - это LT zma = Either a (m (z → a) )
, но это не монада для произвольной монады m
.
Я не уверен, как строго доказать, что у L
нет монадного преобразователя, но нет комбинации конструкторов типов. Either
, ->
и m
кажется, работают правильно.
Таким образом, монада L z
и, как правило, монады формы L m
похоже, не имеют простого и удобного в использовании преобразователя монад, который был бы явным конструктором типа (комбинация Either
, ->
и m
).
- Другой пример монады, которая, кажется, не имеет явного монадного преобразователя:
type S a = (a → Bool) → Maybe a
Эта монада появилась в контексте "поиск монады" здесь. В работе Жюля Хеджеса также упоминается поисковая монада и, в более общем смысле, "выборочные" монады вида
type Sq n q a = (a -> n q) -> n a
для данной монады n
и фиксированного типа q
. Приведенная выше поисковая монада является частным случаем монады выбора с na = Maybe a
и q =()
. Тем не менее, статья Хеджеса (на мой взгляд, неверно) утверждает, что Sq
является монадным преобразователем для монады (a → q) → a
.
Мое мнение таково, что монада (a → q) → a
имеет преобразователь монад (ma → q) → ma
типа "составлено снаружи". Это связано со свойством "жесткости", исследованным в вопросе. Является ли это свойство функтора более сильным, чем монада? А именно, (a → q) → a
является жесткой монадой, и все жесткие монады имеют монадные трансформаторы типа "составной снаружи".
Однако (a → nq) → na
не является жестким, если монада n
является жесткой. Поскольку не все монады являются жесткими (например, Maybe
и Cont
не являются жесткими), монада (a → nq) → na
не будет иметь монадного преобразователя типа "составлено снаружи", (ma → nq) → n (ma)
. Также у него не будет встроенного внутри преобразователя m((a → nq) → na)
- это не монада для произвольной монады m
; возьми m = Maybe
для контрпример. Тип (a → m (nq)) → m (na)
аналогично не является монадой для произвольных монад m
и n
. Тип m(a → nq) → na
является монадой для любого m
но не допускает подъема ma → m (a → nq) → na
потому что мы не можем вычислить значение na
учитывая только некоторые значения, упакованные в произвольную монаду m
.
И S
и Sq
- законные монады (я проверил это вручную), но ни у одного из них нет законного монадного преобразователя.
Вот эвристический аргумент в пользу несуществования монадного преобразователя. Если бы существовало определение типа данных для преобразователя монад (a → nq) → na
которое работает для всех монад n
, то определение типа данных дало бы преобразователь "составной снаружи" для жесткого n
и некоторый другой преобразователь для нежесткий n
. Но такой выбор невозможен для выражения типа, которое использует n
естественным и параметрическим образом (т.е. как непрозрачный конструктор типа с экземпляром монады).
-
Как правило, трансформированные монады сами не обладают монадным трансформером. То есть, как только мы берем некоторую внешнюю монаду m
и применяем к ней какой-то монадный трансформатор t
, мы получаем новую монаду tm
, и у этой монады нет трансформатора: учитывая новую иностранную монаду n
, мы не знаем, как преобразование n
с монады tm
. Если мы знаем преобразователь mt
для монады m
, мы можем сначала преобразовать n
с помощью mt
а затем преобразовать результат с помощью t
. Но если у нас нет преобразователя для монады m
, мы застряли: нет конструкции, которая создает преобразователь для монады tm
из-за знания t
и работает для произвольных иностранных монад m
.
-
Ответ @JamesCandy предполагает, что для любой монады (включая IO
?!) Можно написать (общее, но сложное) выражение типа, представляющее соответствующий преобразователь монад. А именно, вам сначала нужно закодировать Church тип вашего монады, который делает тип похожим на монаду продолжения, а затем определить его преобразователь монад, как будто для монады продолжения. Но я думаю, что это неправильно - это не дает рецепт для производства монадного трансформатора в целом.
Принимая кодирование церкви типа a
значит записывать тип
type ca = forall r. (a -> r) -> r
Этот тип ca
вполне изоморфно по Йонеды леммы. a
До сих пор мы не достигли ничего, кроме того, что сделали тип намного более сложным, введя количественный параметр типа для forall r
.
Теперь пусть Черч-закодирует базовую монаду L
:
type CL a = forall r. (L a -> r) -> r
Опять же, мы ничего не достигли, поскольку CL a
полностью эквивалентен L a
.
Теперь представьте на секунду, что CL a
является монадой продолжения (а это не так!), И запишите монадный преобразователь, как если бы он был монадой продолжения, заменив тип результата r
на mr
:
type TCL m a = forall r. (L a -> m r) -> m r
Утверждается, что это "Церковно-закодированный монадный трансформатор" для L
Но это кажется неправильным. Нам нужно проверить свойства:
-
TCL m
является законной монадой для любой иностранной монады m
и для любой базовой монады L
-
ma → TCL ma
- законный монадический морфизм
Второе свойство выполнено, но я считаю, что первое свойство не выполняется, иными словами, TCL m
не является монадой для произвольной монады m
. Возможно, некоторые монады m
признать это, но другие этого не делают. Я не смог найти общий экземпляр монады для TCL m
соответствующий произвольной базовой монаде L
Другой способ доказать, что TCL m
не является вообще монадой, - это заметить, что для forall r. (a → mr) → mr
forall r. (a → mr) → mr
действительно является монадой для любого конструктора типа m
. Обозначим эту монаду CM
. Теперь TCL ma = CM (L a)
. Если бы TCL m
была монадой, это означало бы, что CM
может быть составлен из любой монады L
и дает законную монаду CM (L a)
. Однако маловероятно, что нетривиальная монада CM
(в частности, не эквивалентная Reader
) будет составлена из всех монад L
Монады обычно не сочиняют без строгих дополнительных ограничений.
Конкретный пример, где это не работает, для читателей монад. Рассмотрим L a = r → a
и ma = s → a
где r
и s
- некоторые фиксированные типы. Теперь мы хотели бы рассмотреть "Церковно-закодированный монадный трансформатор" для всего forall t. (L a → mt) → mt
forall t. (L a → mt) → mt
. Мы можем упростить выражение этого типа, используя лемму Йонеды,
forall t. (x -> t) -> Q t = Q x
(для любого функтора Q
) и получить
forall t. (L a -> s -> t) -> s -> t
= forall t. ((L a, s) -> t) -> s -> t
= s -> (L a, s)
= s -> (r -> a, s)
Так что в данном случае это выражение типа для TCL ma
. Если бы TCL
был монадным преобразователем, то P a = s → (r → a, s)
была бы монадой. Но можно явно проверить, что этот P
на самом деле не является монадой (нельзя реализовать return
и bind
которые удовлетворяют законам).
Даже если это сработало (т. Е. Предположив, что я допустил ошибку, заявив, что TCL m
в целом не является монадой), эта конструкция имеет определенные недостатки:
- Он не является функторным (то есть не ковариантным) по отношению к внешней монаде
m
, поэтому мы не можем делать такие вещи, как интерпретировать преобразованную свободную монаду в другую монаду или объединить два преобразователя монад, как описано здесь. Существует ли принципиальный способ составления двух преобразователей монад если они разного типа, но лежащая в их основе монада того же типа? - Наличие
forall r
делает тип довольно сложным для рассуждений и может привести к снижению производительности (см. Статью "Кодирование Церковью считается опасным") и переполнению стека (поскольку кодирование Церковью обычно небезопасно для стека) - Кодированный Церковью преобразователь монад для базовой монады тождества (
L = Id
) не дает неизмененной внешней монады: T ma = forall r. (a → mr) → mr
T ma = forall r. (a → mr) → mr
и это не то же самое, что ma
. На самом деле, довольно сложно понять, что это за монада, учитывая монаду m
.
В качестве примера, показывающего, почему forall r
усложняет рассуждение, рассмотрим иностранную монаду ma = Maybe a
и попытайтесь понять, что такое тип forall r. (a → Maybe r) → Maybe r
forall r. (a → Maybe r) → Maybe r
самом деле означает forall r. (a → Maybe r) → Maybe r
. Я не смог упростить этот тип или найти хорошее объяснение о том, что этот тип делает, то есть о том, какой "эффект" он представляет (поскольку он является монадой, он должен представлять какой-то "эффект") и как можно использовать такой тип.
-
ReaderT
WriterT
EitherT
преобразователь не эквивалентен стандартным хорошо известным ReaderT
преобразователям, таким как ReaderT
, WriterT
, EitherT
, StateT
и так далее.
Не ясно, сколько существует других монадных трансформаторов и в каких случаях можно было бы использовать тот или иной трансформатор.
- Один из вопросов в посте состоит в том, чтобы найти явный пример монады
m
которая имеет два преобразователя t1
и t2
, так что для некоторой внешней монады n
монады t1 n
и t2 n
не эквивалентны.
Я полагаю, что монада Search
дает такой пример.
type Search a = (a -> p) -> a
где p
- фиксированный тип.
Трансформаторы
type SearchT1 n a = (a -> n p) -> n a
type SearchT2 n a = (n a -> p) -> n a
Я проверил, что SearchT1 n
и SearchT2 n
являются законными монадами для любой монады n
. У нас есть подъемы na → SearchT1 na
и na → SearchT2 na
которые работают, возвращая постоянные функции (просто возвращаем na
как дано, игнорируя аргумент). У нас есть SearchT1 Identity
и SearchT2 Identity
явно эквивалентны Search
.
Большая разница между SearchT1
и SearchT2
заключается в том, что SearchT1
не является функториальной по n
, а SearchT2
- это. Это может иметь значение для "запуска" ("интерпретации") преобразованной монады, поскольку обычно мы хотели бы иметь возможность поднять интерпретатор na → n' a
в "бегущий" SearchT na → SearchT n' a
. Это возможно только с SearchT2
.
Подобный недостаток присутствует в стандартных монадных преобразователях для монады продолжения и монады кодирования: они не являются функториальными в чужой монаде.