Монады как дополнения

Я читал о монадах в теории категорий. Одно определение монад использует пару присоединенных функторов. Монада определяется путем обращения туда и обратно с использованием этих функторов. По-видимому, присоединения очень важны в теории категорий, но я не видел никакого объяснения монад Haskell в терминах присоединенных функторов. Кто-нибудь задумался?


person Bartosz Milewski    schedule 15.01.2011    source источник
comment
Отличный вопрос, мне самому было любопытно.   -  person Tom Crockett    schedule 15.01.2011
comment
Я пытаюсь выяснить, что на самом деле представляют собой эти два (сопряженных) функтора вообще для монады... Так что я тоже был бы признателен за ответ на ваш вопрос! В настоящее время я просматриваю книгу Маклейна, поэтому, если я найду ответ, я сразу же опубликую.   -  person Alp Mestanogullari    schedule 15.01.2011
comment
Я заметил, что в большинстве примеров первый функтор переходит в более богатую категорию с большей структурой, а второй — забывчивый. Таким образом, монада, которая объединяет их в обход, каким-то образом имеет следы более богатой структуры. Моя аналогия была бы такой: начните с жизни в кембрийской эпохе, сопоставьте ее с нашей текущей экосистемой, используя функтор Evolution, а затем сопоставьте обратно, используя какой-нибудь забывчивый функтор. Вы получаете монаду, описывающую различные планы тела животных (все они были созданы во время кембрийского взрыва). Монады как планы тела для таких вещей, как группы, алгебры и т. д.   -  person Bartosz Milewski    schedule 15.01.2011
comment
Возможно, этот вопрос больше подходит для Programers.stackexchange.com...?   -  person stakx - no longer contributing    schedule 16.01.2011
comment
Я хотел бы видеть больше монад, разложенных на присоединенные функторы. Меня интересуют Идентичность, Константа, Читатель, Писатель, Список, Дерево, Может быть, Любой, Свободный и Вероятность. State и Cont уже есть в ответах.   -  person phischu    schedule 07.08.2014


Ответы (5)


Редактировать: просто для удовольствия, я сделаю это правильно. Оригинальный ответ сохранен ниже

Текущий код дополнения для дополнительных категорий теперь находится в пакете дополнений: http://hackage.haskell.org/package/adjunctions

Я просто собираюсь работать с монадой состояния явно и просто. Этот код использует Data.Functor.Compose из пакета transforms, но в остальном является автономным.

Присоединение между f (D -> C) и g (C -> D), записываемое f -> | г., можно охарактеризовать несколькими способами. Мы будем использовать описание counit/unit (epsilon/eta), которое дает два естественных преобразования (морфизмы между функторами).

class (Functor f, Functor g) => Adjoint f g where
     counit :: f (g a) -> a
     unit   :: a -> g (f a)

Обратите внимание, что «a» в counit на самом деле является тождественным функтором в C, а «a» в unit на самом деле является тождественным функтором в D.

Мы также можем восстановить определение присоединения hom-set из определения counit/unit.

phiLeft :: Adjoint f g => (f a -> b) -> (a -> g b)
phiLeft f = fmap f . unit

phiRight :: Adjoint f g => (a -> g b) -> (f a -> b)
phiRight f = counit . fmap f

В любом случае, теперь мы можем определить монаду из нашего соединения unit/counit следующим образом:

instance Adjoint f g => Monad (Compose g f) where
    return x = Compose $ unit x
    x >>= f  = Compose . fmap counit . getCompose $ fmap (getCompose . f) x

Теперь мы можем реализовать классическое соединение между (a,) и (a ->):

instance Adjoint ((,) a) ((->) a) where
    -- counit :: (a,a -> b) -> b
    counit (x, f) = f x
    -- unit :: b -> (a -> (a,b))
    unit x = \y -> (y, x)

А теперь синоним типа

type State s = Compose ((->) s) ((,) s)

И если мы загрузим это в ghci, мы сможем подтвердить, что State — это именно наша классическая монада состояния. Обратите внимание, что мы можем взять противоположную композицию и получить Costate Comonad (она же магазинная комонада).

Есть множество других дополнений, которые мы можем превратить в монады таким образом (такие как (Bool,) Pair), но это своего рода странные монады. К сожалению, мы не можем сделать дополнения, которые вызывают Reader и Writer непосредственно в Haskell, приятным способом. Мы можем сделать Cont, но, как описывает копампкин, для этого требуется дополнение из противоположной категории, поэтому на самом деле используется другая «форма» класса типов «Adjoint», которая переворачивает некоторые стрелки. Эта форма также реализована в другом модуле пакета дополнений.

этот материал по-другому освещен в статье Дерека Элкинса в The Monad Reader 13 -- Расчет монад с помощью теории категорий: http://www.haskell.org/wikiupload/8/85/TMR-Issue13.pdf

Кроме того, в недавней статье Хинце «Расширения Кана для оптимизации программ» рассматривается построение монады списка из присоединения между Mon и Set: http://www.cs.ox.ac.uk/ralf.hinze/Kan.pdf


Старый ответ:

Две ссылки.

1) Category-extras доставляет, как всегда, представление о присоединениях и о том, как из них возникают монады. Как обычно, хорошо подумать, но довольно мало документации: http://hackage.haskell.org/packages/archive/category-extras/0.53.5/doc/html/Control-Functor-Adjunction.html

2) -Кафе также предлагает многообещающее, но краткое обсуждение роли дополнения. Некоторые из них могут помочь в интерпретации дополнительных категорий: http://www.haskell.org/pipermail/haskell-cafe/2007-December/036328.html

person sclv    schedule 15.01.2011
comment
Вы были достаточно любезны, чтобы добавить сигнатуры типов для конкретного экземпляра Adjoint, но я считаю, что это должно быть unit :: b -> (a -> (a,b)). - person phischu; 25.03.2014
comment
ах, я думаю, что это имеет больше смысла. - person sclv; 26.03.2014

Недавно за обедом Дерек Элкинс показывал мне, как монада Cont возникает из компоновки (_ -> k) контравариантного функтора с самой собой, поскольку она оказывается самосопряженной. Вот как вы получаете (a -> k) -> k из этого. Однако его объединение приводит к устранению двойного отрицания, что невозможно написать на Haskell.

Некоторый код Agda, который иллюстрирует и доказывает это, см. на http://hpaste.org/68257.

person copumpkin    schedule 15.01.2011
comment
Не могли бы вы предоставить более подробную информацию? Я не ожидал, что функторы будут жить в Хаске. Больше похоже на то, что один функтор выходит из хаска в какую-то другую категорию, а другой возвращается. Могги, например, насколько я могу судить, определяет функторы для некоего метаязыка. - person Bartosz Milewski; 15.01.2011
comment
@BartoszMilewski: только что решил вернуться к этому вопросу! Я построил доказательство Agda, объясняющее его лучше: hpaste.org/68257 . Я также собираюсь сделать еще несколько примеров, объясняющих, из каких пар сопряженных функторов возникают другие распространенные монады Haskell. - person copumpkin; 09.05.2012

Это старая ветка, но вопрос показался мне интересным, поэтому я сам сделал некоторые расчеты. Надеюсь, Бартош все еще там и может прочитать это..

На самом деле конструкция Эйленберга-Мура дает в этом случае очень ясную картину. (Я буду использовать нотацию CWM с синтаксисом, подобным Haskell)

Пусть T будет монадой списка < T,eta,mu > (eta = return и mu = concat) и рассмотрим Т-алгебру h:T a -> a.

(Обратите внимание, что T a = [a] — это свободный моноид <[a],[],(++)>, то есть тождество [] и умножение (++).)

По определению h должно удовлетворять h.T h == h.mu a и h.eta a== id.

Теперь несложная погоня за диаграммой доказывает, что h на самом деле индуцирует моноидную структуру на a (определяемую x*y = h[x,y] ), и что h становится моноидным гомоморфизмом для этой структуры.

И наоборот, любая моноидная структура < a,a0,* >, определенная в Haskell, естественным образом определяется как T-алгебра.

Таким образом (h = foldr ( * ) a0, функция, которая «заменяет» (:) на (*) и сопоставляет [] с a0, идентификатором).

Итак, в данном случае категория Т-алгебр — это как раз категория моноидных структур, определимых в Haskell, HaskMon.

(Пожалуйста, проверьте, что морфизмы в T-алгебрах на самом деле являются моноидными гомоморфизмами.)

Он также характеризует списки как универсальные объекты в HaskMon, точно так же, как бесплатные продукты в Grp, полиномиальные кольца в CRng и т. д.

Адъюкция, соответствующая приведенной выше конструкции, < F,G,eta,epsilon >

где

  • F:Hask -> HaskMon, который переводит тип a в «свободный моноид, сгенерированный a», то есть [a],
  • G:HaskMon -> Hask, забывчивый функтор (забудьте умножение),
  • eta:1 -> GF , естественное преобразование, определяемое \x::a -> [x],
  • epsilon: FG -> 1 , естественное преобразование, определяемое приведенной выше функцией складывания («каноническая сюръекция» от свободного моноида к его частному моноиду)

Далее идет еще одна «категория Клейсли» и соответствующее дополнение. Вы можете проверить, что это просто категория типов Haskell с морфизмами a -> T b, где их композиции задаются так называемой «композицией Клейсли» (>=>). Типичный программист на Haskell найдет эту категорию более знакомой.

Наконец, как показано в CWM, категория T-алгебр (соответственно категория Клейсли) становится терминальным (соответственно начальным) объектом в категории адъюкций, определяющих списочную монаду T в подходящем смысле.

Я предлагаю сделать аналогичные вычисления для функтора бинарного дерева T a = L a | B (T a) (T a), чтобы проверить ваше понимание.

person takosuke    schedule 26.02.2011
comment
Я все еще здесь. Спасибо за объяснение. Это похоже на то, что я интуитивно понял из конструкции Эйленберга-Мура, за исключением того, что ваше описание намного лучше и более подробно. Проблема в том, что это не приводит к лучшему пониманию роли монад в Haskell. Монада списка предназначена для описания недетерминированных функций. Если бы кто-нибудь мог показать конструкцию категории недетерминированных функций и показать, что сопряжение между ней и Хаском порождает монаду списка, я был бы очень впечатлен. - person Bartosz Milewski; 03.03.2011
comment
@BartoszMilewski Возможно, в какой-то момент за последние 9 лет это пришло вам в голову, но это буквально то, что делает для вас версия списка дополнения Клейсли. Он демонстрирует примыкание между категорией типов с обычными функциями Haskell между ними и категорией типов с недетерминированными функциями между ними. Ваше здоровье - person HaskellLearner; 24.05.2020

Я нашел стандартные конструкции вспомогательных функторов для любой монады Эйленберга-Мура, но я не уверен, что это проясняет проблему. Вторая категория в конструкции — это категория Т-алгебр. Алгебра T добавляет «произведение» к начальной категории.

Итак, как это будет работать для монады списка? Функтор в монаде списка состоит из конструктора типа, например, Int->[Int], и отображения функций (например, стандартного применения map к спискам). Алгебра добавляет отображение из списков в элементы. Одним из примеров может быть сложение (или умножение) всех элементов списка целых чисел. Функтор F принимает любой тип, например, Int, и отображает его в алгебру, определенную в списках Int, где произведение определяется монадическим соединением (или наоборот, соединение определяется как произведение). Забывающий функтор G берёт алгебру и забывает произведение. Затем пара F, G сопряженных функторов используется для построения монады обычным способом.

Я должен сказать, что я не мудрее.

person Bartosz Milewski    schedule 15.01.2011

Если вам интересно, вот некоторые мысли неспециалиста о роли монад и дополнений в языках программирования:

Во-первых, для данной монады T существует единственное присоединение к категории Клейсли монады T. В Haskell использование монад в первую очередь ограничено операциями в этой категории (которая, по сути, является категорией свободных алгебр, без частных). Фактически, все, что можно сделать с монадой Haskell, — это составить несколько морфизмов Клейсли типа a->T b с помощью выражений do, (>>=) и т. д. для создания нового морфизма. В этом контексте роль монад ограничивается просто экономией обозначений. Ассоциативность морфизмов используется для того, чтобы иметь возможность писать (скажем) [0,1,2] вместо (Cons 0 (Cons 1 (Cons 2 Nil))), то есть вы можете писать последовательность как последовательность, а не как дерево.

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

Это мой ответ на ваш первоначальный вопрос, но мне любопытно, что об этом говорят эксперты Haskell.

С другой стороны, как мы уже отмечали, между монадами и дополнениями к (T-)алгебрам также существует соответствие 1-1. Сопряжения, в терминах Маклейна, — это «способ выражения эквивалентности категорий». В типичной настройке дополнений <F,G>:X->A, где F является своего рода «генератором свободной алгебры», а G — «забывающим функтором», соответствующая монада будет (посредством использования Т-алгебр) описывать, как (и когда) алгебраическая структура A построен на объектах X.

В случае Hask и монады списка T структура, которую вводит T, является структурой моноида, и это может помочь нам установить свойства (включая правильность) кода с помощью алгебраических методов, которые предоставляет теория моноидов. Например, функцию foldr (*) e::[a]->a можно легко рассматривать как ассоциативную операцию, если <a,(*),e> является моноидом, и компилятор может использовать этот факт для оптимизации вычислений (например, за счет параллелизма). Другое приложение состоит в том, чтобы идентифицировать и классифицировать «шаблоны рекурсии» в функциональном программировании с использованием категориальных методов в надежде (частично) избавиться от «перехода к функциональному программированию», Y (произвольный комбинатор рекурсии).

По-видимому, такого рода приложения являются одним из основных мотивов создателей теории категорий (Маклейн, Эйленберг и др.), а именно установить естественную эквивалентность категорий и перенести известный метод из одной категории в другую (например, гомологические методы к топологическим пространствам, алгебраические методы к программированию и др.). Здесь сопряжения и монады являются незаменимыми инструментами для использования этой связи категорий. (Между прочим, понятие монады (и двойственного ей комонады) настолько общее, что можно даже зайти так далеко, чтобы определить «когомологии» типов Haskell. Но я пока не задумывался.)

Что касается недетерминированных функций, которые вы упомянули, я могу сказать гораздо меньше... Но обратите внимание на это; если дополнение <F,G>:Hask->A для некоторой категории A определяет монаду списка T, должен существовать уникальный "функтор сравнения" K:A->MonHask (категория моноидов, определяемых в Haskell), см. CWM. Фактически это означает, что ваша интересующая категория должна быть категорией моноидов в некоторой ограниченной форме (например, в ней могут отсутствовать некоторые частные, но не свободные алгебры), чтобы определить монаду списка.

Наконец, некоторые замечания:

Функтор бинарного дерева, о котором я упоминал в своем последнем посте, легко обобщается на произвольный тип данных T a1 .. an = T1 T11 .. T1m | .... А именно, любой тип данных в Haskell естественным образом определяет монаду (вместе с соответствующей категорией алгебр и категорией Клейсли), которая является просто результатом тотальности любого конструктора данных в Haskell. Это еще одна причина, по которой я считаю класс Monad в Haskell не более чем синтаксическим сахаром (что, конечно, очень важно на практике).

person tokosuke    schedule 05.03.2011