Есть ли монада, у которой нет соответствующего монадного трансформатора (кроме IO)?

До сих пор каждая монада (которая может быть представлена ​​как тип данных), с которой я столкнулась, имела соответствующий трансформатор монады или могла иметь ее. Есть ли такая монада, которая не может иметь ее? Или все монады имеют соответствующий трансформатор?

Трансформатором t, соответствующим monad m, я имею в виду, что t Identity изоморфен m. И, конечно же, он удовлетворяет законам трансформатора монады и что t n является монадой для любой монады n.

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

В качестве последующего вопроса существует ли монада m, имеющая два разных трансформатора t1 и t2? То есть t1 Identity изоморфно t2 Identity и m, но существует монада n такая, что t1 n не изоморфна t2 n.

(IO и ST имеют специальную семантику, поэтому я не учитываю их здесь и полностью игнорирую их). Пусть сосредоточены только на "чистых" монадах, которые могут быть построены с использованием типов данных.)

Ответ 1

Я с @Rhymoid на этом, я думаю, что у всех монад есть два (!!) трансформатора. Моя конструкция немного отличается и гораздо менее завершена. Я хотел бы иметь возможность взять этот набросок в качестве доказательства, но я думаю, что я либо скучаю по навыкам/интуиции и/или это может быть довольно сложным.

Благодаря Клейсли каждая монада (m) может быть разложена на два функтора F_k и G_k, так что F_k оставлен присоединенным к G_k и m изоморфен G_k * F_k (здесь * - композиция функтора). Кроме того, из-за присоединения F_k * G_k образует комонаду.

Я утверждаю, что t_mk определено так, что t_mk n = G_k * n * F_k является t_mk n = G_k * n * F_k преобразователем. Ясно, что t_mk Id = G_k * Id * F_k = G_k * F_k = m. Определение return для этого функтора несложно, поскольку F_k является "остроконечным" функтором, и определение join должно быть возможным, поскольку extract из comonad F_k * G_k может использоваться для уменьшения значений типа (t_mk n * t_mk n) a = (G_k * n * F_k * G_k * n * F_k) a к значениям типа G_k * n * n * F_k, которые затем дополнительно уменьшаются посредством join из n.

Мы должны быть немного осторожнее, так как F_k и G_k не являются эндофункторами на Hask. Таким образом, они не являются экземплярами стандартного класса типов Functor, а также не могут быть напрямую скомпонованы с n как показано выше. Вместо этого мы должны "спроецировать" n в категорию Клейсли перед составлением, но я считаю, что return от m обеспечивает эту "проекцию".

Я полагаю, что вы также можете сделать это с помощью декомпозиции монады Эйленберга-Мура, задав m = G_em * F_em, tm_em n = G_em * n * F_em и аналогичные конструкции для lift, return и join с аналогичной зависимостью от extract из comonad. F_em * G_em.

Ответ 2

Вот рука-волнистый ответ - не совсем уверен.

Монады можно рассматривать как интерфейс императивных языков. return заключается в том, как вы вводите чистое значение в язык, а >>= - это то, как вы соединяете фрагменты языка вместе. Законы Монады гарантируют, что "рефакторинг" частей языка будет работать так, как вы ожидали. Любые дополнительные действия, предоставляемые монадой, можно рассматривать как "операции".

Monad Transformers - один из способов приблизиться к проблеме "расширяемых эффектов". Если у нас есть Monad Transformer t, который преобразует Monad m, то можно сказать, что язык m расширяется дополнительными операциями, доступными через t. Монада Identity - это язык без эффектов/операций, поэтому применение t to Identity приведет вас к языку только с операциями, предоставляемыми t.

Итак, если мы думаем о Monads в терминах модели "инъекции, сращивания и других операций", тогда мы можем просто переформулировать их с помощью Free Monad Transformer. Даже монада IO могла быть превращена в трансформатор таким образом. Единственный улов в том, что вы, вероятно, хотите каким-то образом очистить этот слой от стека трансформатора в какой-то момент, и единственный разумный способ сделать это - если у вас есть IO в нижней части стека, чтобы вы могли просто выполнить там операции.

Ответ 3

Обновление: утверждения 1 и 2 ниже, скорее всего, неверны; Я думаю, что нашел монадные трансформаторы для этих случаев. Я еще не закончил необходимые расчеты, но они выглядят многообещающими. Заявления 3 и 4 остаются в силе. Я добавил утверждение 5, чтобы показать пример монады с двумя различными трансформаторами.

Преобразователь для Either a (z → a) (наиболее вероятно) n (Either a (z → ma), где m - произвольная инородная монада. Преобразователь для (a → np) → na - (наиболее вероятно) (a → tmp) → tma где tm - преобразователь для монады n.

  1. Я думаю, что нашел по крайней мере один контрпример: простую и явную монаду, которая не имеет простого и явного преобразователя монад.

Конструктор типа монады 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).

  1. Другой пример монады, которая, кажется, не имеет явного монадного преобразователя:

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 естественным и параметрическим образом (т.е. как непрозрачный конструктор типа с экземпляром монады).

  1. Как правило, трансформированные монады сами не обладают монадным трансформером. То есть, как только мы берем некоторую внешнюю монаду m и применяем к ней какой-то монадный трансформатор t, мы получаем новую монаду tm, и у этой монады нет трансформатора: учитывая новую иностранную монаду n, мы не знаем, как преобразование n с монады tm. Если мы знаем преобразователь mt для монады m, мы можем сначала преобразовать n с помощью mt а затем преобразовать результат с помощью t. Но если у нас нет преобразователя для монады m, мы застряли: нет конструкции, которая создает преобразователь для монады tm из-за знания t и работает для произвольных иностранных монад m.

  2. Ответ @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 и так далее.

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

  1. Один из вопросов в посте состоит в том, чтобы найти явный пример монады 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.

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

Ответ 4

Мое решение использует логическую структуру терминов на Haskell и т.д.

Я смотрел на правильные расширения Кан как возможные представления преобразователя монады. Как все знают, правильные расширения Кан - это ограничения, поэтому имеет смысл, что они должны служить универсальным кодированием любого объекта, представляющего интерес. Для монадических функторов F и M я посмотрел на правильное расширение Кан по MF вдоль F.

Сначала я доказал лемму, "катящуюся лемму": внутри него можно свернуть предложенный функтор к расширению Right kan, дав карту F (Ran GH) → Ran G (FH) для любых функторов F, G и H.

Используя эту лемму, я вычислил монадическое объединение для правого расширения Кана Ran F (MF), требуя закона распределения FM → MF. Это выглядит следующим образом:

Ran F(MF) . Ran F(MF) [rolling lemma] =>
  Ran F(Ran F(MF)MF) [insert eta] =>
  Ran F(Ran F(MF)FMF) [gran] =>
  Ran F(MFMF) [apply distributive law] =>
  Ran F(MMFF) [join Ms and Fs] =>
  Ran F(MF).

Что интересно в этой конструкции, так это то, что она допускает подъемы обоих функторов F и M следующим образом:

(1) F [lift into codensity monad] =>
  Ran F F [procompose with eta] =>
  Ran F(MF).

(2) M [Yoneda lemma specialized upon F-] =>
  Ran F(MF).

Я также исследовал правильное расширение Каня Ran F (FM). Кажется, что он немного лучше вел себя, добиваясь монадичности, без обращения к закону распределения, но гораздо более требователен к тому, какие функторы он поднимает. Я определил, что он будет поднимать монадические функторы при следующих условиях:

1) F монадический.

2) F | - U, в этом случае допускается лифт F ~> Ran U (UM). Это можно использовать в контексте монады состояния для "установки" состояния.

3) M при определенных условиях, например, когда M допускает закон распределения.