it-swarm.com.ru

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

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

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

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

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

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

63
Petr Pudlák

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

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

Я утверждаю, что t_mk определен так, что 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 не являются endofunctors на Hask. Таким образом, они не являются экземплярами стандартного класса типов Functor, а также не могут быть напрямую скомпонованы с n, как показано выше. Вместо этого нам нужно "спроецировать" n в категорию Kleisli перед составлением, но я считаю, что return из m обеспечивает эту "проекцию".

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

19
Boyd Stephen Smith Jr.

Вот волнообразный ответ, который я не совсем уверен.

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

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

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

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

Конструктор типа монады L для этого контрпримера определяется как

  type L z a  = Either a (z -> a)

Цель этой монады - украсить монаду обычного читателя z -> a явным значением pure (Left x). Значение pure обычной монады читателя является константной функцией pure x = _ -> x. Однако если нам дано значение типа z -> a, мы не сможем определить, является ли это значение постоянной функцией. При использовании L z a значение pure явно представляется как Left x. Теперь пользователи могут сопоставлять с образцом L z a и определять, является ли данное монадическое значение чистым или имеет эффект. Кроме этого, монада 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 m a = Either a (m a). Эта конструкция украшает данную монаду 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 e m a = m (Either e a), потому что преобразователь монад работает с использованием обхода. Похоже, что любая монада, которая содержит Either в своем типе данных, нуждается в обходе для работы монадного преобразователя, и поэтому в преобразователе должна быть "внутренняя" композиция. Однако преобразователь монад Reader требует, чтобы его базовая монада была составлена ​​ вне внешней монады ReaderT r m a = r -> m a. Монада L представляет собой композицию типа данных, которая требует составного внутреннего преобразователя и монады, которая требует составного внешнего преобразователя, а вторая монада находится внутри первого, который невозможно согласовать. Независимо от того, как мы пытаемся определить L-преобразователь LT, кажется, что мы не можем удовлетворить законы монадных преобразователей.

Одна из возможностей определения конструктора типа LT была бы LT z m a = Either a (z -> m a). Результатом является законная монада, но морфизм m a -> LT z m a не сохраняет m 'return, потому что return x отображается в Right (\z -> return x), которая не является L's return (всегда Left x).

Другая возможность - LT z m a = z -> Either a (m a). В результате получается монада, но опять же m's return отображается в \_ -> Right (...) вместо Left (...), как требуется для монады z -> Either a (m a).

Еще одна возможность объединения доступных конструкторов типов - это LT z m a = 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. Приведенная выше поисковая монада является частным случаем монады выбора с n a = Maybe a и q = (). Тем не менее, статья Хеджеса (на мой взгляд, неверно) утверждает, что Sq является монадным преобразователем для монады (a -> q) -> a.

Мое мнение таково, что монада (a -> q) -> a имеет преобразователь монад (m a -> q) -> m a типа "составлено снаружи". Это связано со свойством "жесткости", которое исследуется в вопросе Является ли это свойство функтора более сильным, чем монада? А именно, (a -> q) -> a является жесткой монадой, и все жесткие монады имеют монадные преобразователи " составной снаружи "тип.

Однако (a -> n q) -> n a не является жестким, если только монада n не является жесткой. Поскольку не все монады являются жесткими (например, Maybe и Cont не являются жесткими), монада (a -> n q) -> n a не будет иметь монадного преобразователя типа "составленный снаружи", (m a -> n q) -> n (m a). Также у него не будет встроенного внутри преобразователя m((a -> n q) -> n a) - это не монада для произвольной монады m; возьмите m = Maybe для контрпример. Тип (a -> m (n q)) -> m (n a) также не является монадой для произвольных монад m и n. Тип m(a -> n q) -> n a является монадой для любого m, но не допускает отмены m a -> m (a -> n q) -> n a, потому что мы не можем вычислить значение n a, учитывая только некоторые значения, заключенные в произвольную монаду m.

И S, и Sq являются законными монадами (я проверил это вручную), но ни у одного из них нет законного преобразователя монад.

Вот эвристический аргумент в пользу несуществования монадного преобразователя. Если бы существовало определение типа данных для преобразователя монад (a -> n q) -> n a, которое работает для всех монад n, то определение этого типа данных привело бы к преобразователю "составной снаружи" для жесткого n и некоторому другому преобразователю для нежесткого n. Но такой выбор невозможен для выражения типа, которое использует n естественно и параметрически (то есть как непрозрачный конструктор типа с экземпляром монады).

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

 type TCL m a = forall r. (L a -> m r) -> m r

Утверждается, что это "церковный закодированный монадный преобразователь" для L. Но это кажется неправильным. Нам нужно проверить свойства:

  • TCL m является законной монадой для любой иностранной монады m и для любой базовой монады L
  • m a -> TCL m a - законный монадический морфизм

Второе свойство сохраняется, но я считаю, что первое свойство не выполняется, другими словами, TCL m не является монадой для произвольной монады m. Возможно, некоторые монады m допускают это, а другие нет. Мне не удалось найти общий экземпляр монады для TCL m, соответствующий произвольной базовой монаде L.

Другой способ доказать, что TCL m в общем случае не является монадой, это отметить, что forall r. (a -> m r) -> m r действительно является монадой для любого конструктора типа m. Обозначим эту монаду CM. Теперь TCL m a = CM (L a). Если бы TCL m была монадой, это означало бы, что CM может быть составлено с любой монадой L и дает законную монаду CM (L a). Однако весьма маловероятно, что нетривиальная монада CM (в частности, не эквивалентная Reader) будет составлена ​​из всех монад L. Монады обычно не сочиняют без строгих дополнительных ограничений.

Конкретный пример, где это не работает, для читателей монад. Рассмотрим L a = r -> a и m a = s -> a, где r и s являются некоторыми фиксированными типами. Теперь мы хотели бы рассмотреть "Церковно-закодированный монадный преобразователь" forall t. (L a -> m t) -> m t. Мы можем упростить выражение этого типа, используя лемму Йонеды,

 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 m a. Если бы TCL был преобразователем монад, то P a = s -> (r -> a, s) был бы монадой. Но можно явно проверить, что это P на самом деле не является монадой (нельзя реализовать return и bind, которые удовлетворяют законам).

Даже если это сработало (т.е. , если предположить, что я допустил ошибку, заявив, что TCL m в общем случае не является монадой ), эта конструкция имеет определенные недостатки:

  • Он не является функторным (т.е. не ковариантным) по отношению к внешней монаде m, поэтому мы не можем делать такие вещи, как интерпретировать преобразованную свободную монаду в другую монаду или объединить два преобразователя монад, как описано здесь Есть принципиальный способ составить два монадных трансформатора, если они разного типа, но лежащая в их основе монада того же типа?
  • Наличие forall r делает тип достаточно сложным для рассуждения и может привести к снижению производительности (см. Статью "Кодирование Церковью считается опасным") и переполнению стека (поскольку кодирование Церковью обычно небезопасно для стека)
  • Кодированный Церковью преобразователь монад для базовой монады идентификаторов (L = Id) не приводит к неизмененной внешней монаде: T m a = forall r. (a -> m r) -> m r, и это не то же самое, что m a. На самом деле, довольно сложно понять, что это за монада, учитывая монаду m.

В качестве примера, показывающего, почему forall r усложняет рассуждение, рассмотрим внешнюю монаду m a = Maybe a и постараемся понять, что на самом деле означает тип forall r. (a -> Maybe r) -> Maybe r. Я не смог упростить этот тип или найти хорошее объяснение того, что этот тип делает, то есть, какой "эффект" он представляет (так как это монада, он должен представлять какой-то "эффект") и как можно было бы использовать такой тип.

  • Кодируемый Церковью монадный преобразователь не эквивалентен стандартным хорошо известным монадным преобразователям, таким как ReaderT, WriterT, EitherT, StateT и так далее.

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

1
winitzki

Мое решение использует логическую структуру терминов Haskell. Всем известно, что функцию в Haskell с типом возврата t можно превратить в монадическую функцию с типом возврата (Monad m) => m t. Следовательно, если функция "связать" могла бы соответствующим образом "монодифицировать" свой программный текст, результатом был бы преобразователь монад.

Камнем преткновения является то, что нет никаких причин, по которым "монадификация" оператора "связывания" должна удовлетворять законам, в частности ассоциативности. Вот тут-то и возникает исключение среза. Теорема об исключении среза влияет на программный текст, заключающий в себе все привязки, анализ случаев и приложения. Кроме того, все вычисления для конкретного термина выполняются в одном месте.

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

Это действительно шерстяное, вот пример. Рассмотрим следующую строгую монаду списка:

rseq x y = case x of
    x:xs -> (x:xs) : y
    [] -> [] : y


evalList (x:xs) = rseq x (evalList xs)
evalList [] = []

instance Monad [] where
    return x = [x]
    ls >>= f = concat (evalList (map f ls))

Этот порядок оценки приводит к стандартному ListT (на самом деле не монада). Тем не менее, путем устранения сокращения:

instance Monad [] where
    return x = [x]
    ls >>= f = case ls of
        [] -> []
        x:xs -> case f x of
            y:ys -> (y:ys) ++ (xs >>= f)
            [] -> [] ++ (xs >>= f)

Это обеспечивает точный порядок оценки, подлежащий "монадификации".


В ответ Петру Пудлаку:

Если тип рассматриваемой монады - это некоторый тип функции (это удобно для кодирования по Чёрчу всех типов данных), то тип функции конвертируется путем украшения всех возвращаемых значений этого типа преобразованной монадой. Это типовая часть монадификации. Часть значения монадификации поднимает чистые функции, используя "возврат", и объединяет их с использованием жителей типа монады, используя "связывание", сохраняя порядок оценки исходного текста программы.

Монада строгого списка приведена в качестве примера порядка оценки, который не составляется ассоциативно, о чем свидетельствует тот факт, что ListT использует тот же порядок оценки, что и монада строгого списка.

Чтобы завершить пример, церковная кодировка монады списка:

data List a = List (forall b. b -> (a -> List a -> b) -> b)

Monadified, это становится:

data ListT m a = ListT (forall b. m b -> (a -> List a -> m b) -> m b)

cons x xs = \_ f -> f x xs

nil = \x _ -> x

instance (Monad m) => Monad (ListT m) where
    return x = cons x nil
    ls >>= f = ls nil (\x xs -> f x >>= \g ->
        g (liftM (nil ++) (xs >>= f)) (\y ys -> liftM (cons y ys ++) (xs >>= f))

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

[Технически, то, что вам нужно, это исключение срезов аппроксимантов: A - это исключение срезов (аппроксимаций) B, если для каждого конечного аппроксимата B существует конечный аппроксимат A, такой что A является исключением B срезов B. ]

Надеюсь, это поможет.

0
James Candy