Монади як приєднання


79

Я читав про монади в теорії категорій. В одному визначенні монад використовується пара суміжних функторів. Монада визначається в обидва кінці з використанням цих функторів. Очевидно, що приєднання дуже важливі в теорії категорій, але я не бачив жодного пояснення монад Хаскела з точки зору суміжних функторів. Хтось замислювався над цим?


2
Чудове запитання, мені самому було цікаво про це.
Tom Crockett,

Я намагаюся зрозуміти, що насправді є цими двома (суміжними) функторами взагалі для монади ... Тож я був би вдячний за відповідь на ваше запитання! На даний момент я переглядаю книгу MacLane, тому, якщо знайду відповідь, я відразу ж розміщу її.
Alp Mestanogullari

3
Я помітив, що у більшості прикладів перший функтор переходить до розбагатішої категорії з більшою структурою, а другий - забудькувальний. Отже, монада, яка поєднує ці два аспекти в обидва кінці, якось має сліди багатшої структури. Моя аналогія була б такою: Почніть із життя в епоху Кембрію, перекладіть його в нашу сучасну екосистему за допомогою функтора Evolution, ніж поверніть назад, використовуючи якийсь забувливий функтор. Ви отримуєте "монаду", яка описує різні плани тіла тварин (усі вони були створені під час кембрійського вибуху). Монади як "плани тіла" для таких речей, як групи, алгебри тощо
Бартош Мілевський

Можливо, це питання було б кращим для програмістів.stackexchange.com ...?
stakx - більше не вносить вклад

3
Я хотів би бачити більше монад, що розкладаються на суміжні функтори. Мене цікавлять "Ідентичність", "Const", "Читач", "Письменник", "Список", "Дерево", "Можливо", "Вільний" та "Імовірність". State і Cont вже відповідають.
phischu

Відповіді:


40

Редагувати : Просто для розваги я збираюся зробити це правильно. Оригінальна відповідь збережена нижче

Поточний код приєднання для додаткових категорій зараз знаходиться в пакеті приєднань: http://hackage.haskell.org/package/adjunctions

Я просто збираюся працювати через державну монаду явно і просто. Цей код використовується Data.Functor.Composeз пакета трансформаторів, але в іншому випадку є автономним.

Доповнення між f (D -> C) і g (C -> D), написане f - | g, можна охарактеризувати різними способами. Ми використаємо опис count / unit (epsilon / eta), який дає два природні перетворення (морфізми між функторами).

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

Зверніть увагу, що "a" в count дійсно є функтором ідентичності в C, а "a" в одиниці насправді є функтором ідентичності в D.

Ми також можемо відновити визначення приєднання самонабору з визначення кількості / одиниці.

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

У будь-якому випадку, тепер ми можемо визначити Монаду з приєднання нашого підрозділу / рахунку так:

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, ми можемо підтвердити, що держава - це саме наша класична державна монада. Зверніть увагу, що ми можемо взяти протилежний склад і отримати Costate Comonad (він же магазин комонад).

Існує купа інших приєднань, які ми можемо зробити таким чином у монади (наприклад, (Bool,) Pair), але це свого роду дивні монади. На жаль, ми не можемо зробити приєднання, які спонукають Reader і Writer безпосередньо в Haskell приємним чином. Ми можемо зробити Cont, але, як описує copumpkin, для цього потрібне доповнення з протилежної категорії, тому насправді використовується інша "форма" класу типів "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) Додаткові категорії надають, як завжди, представлення про додатки та те, як від них виникають монади. Як зазвичай, добре подумати, але досить мало документації: 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


Ви були люб’язними, щоб додати підписи типу для конкретного екземпляра Adjoint, але я вважаю, що це повинно бути unit :: b -> (a -> (a,b)).
phischu

ах, я думаю, що це має більше сенсу.
sclv

10

Дерек Елкінс нещодавно за вечерею показував мені, як Cont Monad виникає в результаті складання (_ -> k)противаріантного функтора з собою, оскільки це, здається, самоспрямованість. Ось як ти (a -> k) -> kз цього вийдеш. Однак його підрахунок призводить до подвійного усунення заперечень, про що не можна писати в Haskell.

Деякий код Agda, який ілюструє та доводить це, див . На веб-сайті http://hpaste.org/68257 .


1
Не могли б Ви надати більше деталей? Я не обов'язково очікував, що функтори житимуть у Хаску. Більш схожий на одного функтора, який виходить із Хаска, в якусь іншу категорію, а інший повертається. Моджі, наприклад, з того, що я можу зібрати, визначає функтори до якоїсь метамови.
Бартош Мілевський,

@BartoszMilewski: тільки що вирішив переглянути це питання! Я створив доказ Agda, який пояснює це краще: hpaste.org/68257 . Я також збираюся зробити ще кілька з них, пояснюючи, з яких пар суміжних функторів виникають інші поширені монади Хаскелла.
copumpkin

9

Це стара тема, але мені це питання здалося цікавим, тому я зробив кілька обчислень сам. Сподіваємось, Бартош все ще там і може прочитати це ..

Насправді конструкція Ейленберга-Мура дає дуже чітке уявлення в цьому випадку. (Я буду використовувати позначення 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 насправді індукує моноїдну структуру на (визначену x*y = h[x,y]), і що hстає моноїдним гомоморфізмом для цієї структури.

І навпаки, будь-яка моноїдна структура, < a,a0,* >визначена в Хаскеллі, природно визначається як Т-алгебра.

Таким чином ( h = foldr ( * ) a0, функція, яка "замінює" (:)на (*) , і карти []в a0тотожність).

Отже, у цьому випадку категорія Т-алгебр - це просто категорія моноїдних структур, що визначаються в Haskell, HaskMon.

(Будь ласка, перевірте, чи морфізми в Т-алгебрах насправді є моноїдними гомоморфізмами.)

Він також характеризує списки як універсальні об'єкти в 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 , природне перетворення, визначене функцією згортання вище (`` канонічна сюр'єкція '' від вільного моноїда до його часткового моноїду)

Далі існує ще одна „категорія Клейслі” та відповідне доповнення. Ви можете перевірити, що це просто категорія типів Хаскелла з морфізмами a -> T b, де її композиції даються так званою `` композицією Клейслі '' (>=>). Типовий програміст Haskell вважає цю категорію більш звичною.

Нарешті, як проілюстровано в CWM, категорія T-алгебр (відповідно категорія Клейслі) стає кінцевим (відповідно початковим) об'єктом у категорії коригувань, які визначають монаду списку T у відповідному сенсі.

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


Я все ще тут. Дякую за пояснення. Це схоже на те, що я зрозумів із конструкції Ейленберга-Мура, за винятком того, що ваш опис набагато кращий і детальніший. Проблема в тому, що це не призводить до кращого розуміння ролі монад у Хаскелі. Монада списку повинна описувати недетерміновані функції. Якби хтось міг продемонструвати побудову категорії недетермінованих функцій і показати, що приєднання між нею та Hask породжує списку монади, я був би справді вражений.
Бартош Мілевський,

@BartoszMilewski Можливо, у певний момент 9-річного чергування це вам спало на думку, але це буквально те, що для вас робить версія списку додатку Клейслі. Він демонструє суміжність між категорією типів із звичайними функціями Хаскелла між ними та категорією типів з недетермінованими функціями між ними. Привітання
HaskellLearner

2

Я знайшов стандартні конструкції додаткових функторів для будь-якої монади Ейленберга-Мура, але я не впевнений, що це додає якесь розуміння проблеми. Друга категорія в побудові - це категорія Т-алгебр. Алгебра AT додає "товар" до початкової категорії.

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

Треба сказати, що я не мудріший.


1

Якщо вам цікаво, ось кілька думок непрофесіонала щодо ролі монад та приєднань у мовах програмування:

Перш за все, існує для даної монади Tунікальне доповнення до категорії Клейслі T. У Хаскелі використання монад в першу чергу обмежується операціями в цій категорії (яка, по суті, є категорією вільних алгебр, без коефіцієнтів). Насправді, все, що можна зробити з монадою Хаскелла, - це скласти деякі морфізми Клейслі типу a->T bза допомогою виразів do (>>=)тощо тощо, щоб створити новий морфізм. У цьому контексті роль монад обмежується лише економією позначень. Один використовує асоціативність морфізмів, щоб мати можливість писати (скажімо) [0,1,2] замість (Cons 0 (Cons 1 (Cons 2 Nil))), тобто ви можете писати послідовність як послідовність, а не як дерево.

Навіть використання монад IO не є суттєвим, оскільки нинішня система типу Хаскелл є досить потужною для реалізації інкапсуляції даних (екзистенціальні типи).

Це моя відповідь на ваше початкове запитання, але мені цікаво, що говорять з цього приводу експерти Haskell.

З іншого боку, як ми вже зазначали, існує також відповідність 1-1 між монадами та приєднаннями до (T-) алгебр. З точки зору Маклейна, приєднання - це "спосіб виразити еквівалентність категорій". У типовій обстановці приєднань, <F,G>:X->Aде Fє якийсь `` генератор вільної алгебри '', а G `` забувливий функтор '', відповідна монада (за допомогою Т-алгебр) опише, як (і коли) Aбудується алгебраїчна структура об'єкти X.

У випадку з Hask і списком монади T, структура, яку Tвводить, - це структура моноїдів, і це може допомогти нам встановити властивості (включаючи правильність) коду за допомогою алгебраїчних методів, які забезпечує теорія моноїдів. Наприклад, функцію foldr (*) e::[a]->aможна легко розглядати як асоціативну операцію, якщо <a,(*),e>вона є моноїдом, що може бути використано компілятором для оптимізації обчислень (наприклад, паралелізмом). Інший додаток полягає у виявленні та класифікації "шаблонів рекурсії" у функціональному програмуванні за допомогою категоріальних методів з надією (частково) розпорядитися "переходом до функціонального програмування" Y (довільний комбінатор рекурсії).

Очевидно, подібного роду програми є однією з основних мотивацій творців теорії категорій (MacLane, Eilenberg та ін.), А саме встановити природну еквівалентність категорій та перенести добре відомий метод з однієї категорії в іншу (наприклад, гомологічні методи до топологічних просторів, алгебраїчні методи програмування тощо). Тут приєднання та монади є необхідними інструментами для використання цього зв'язку категорій. (До речі, поняття монад (і їх двоїстих комонад) настільки загальне, що можна навіть зайти так далеко, щоб визначити "когомології" типів Хаскелла. Але я ще не замислювався.)

Щодо недетермістичних функцій, про які ви згадали, я маю сказати набагато менше ... Але зауважте, що; якщо доповнення <F,G>:Hask->Aдо якоїсь категорії Aвизначає монаду списку T, повинен бути унікальний "функтор порівняння" K:A->MonHask(категорія моноїдів, що визначається в Хаскелі), див. CWM. Це фактично означає, що ваша категорія інтересів повинна бути категорією моноїдів у певній обмеженій формі (наприклад, у ній можуть бути відсутні коефіцієнти, але не вільні алгебри) для того, щоб визначити монаду списку.

Нарешті, кілька зауважень:

Двійковий функтор дерева, про який я згадав у своєму останньому розміщенні, легко узагальнює довільний тип даних T a1 .. an = T1 T11 .. T1m | .... А саме, будь-який тип даних у Хаскелі, природно, визначає монаду (разом із відповідною категорією алгебр та категорією Клейслі), що є лише результатом того, що будь-який конструктор даних у Хаскеллі є загальним. Це ще одна причина, чому я вважаю, що клас Монади Хаскелла - це не набагато більше, ніж синтаксичний цукор (що, звичайно, досить важливо на практиці).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.