Монада - це просто моноїд у категорії ендофандерів, у чому проблема?


722

Хто перший сказав таке?

Монада - це просто моноїд у категорії ендофандерів, у чому проблема?

І на менш важливу увагу, чи це правда, і якщо так, ви можете дати пояснення (сподіваємось, таке, яке може зрозуміти той, хто не має багато досвіду Haskell)?


14
Дивіться "Категорії для працюючого математика"
Дон Стюарт

18
Вам не потрібно це розуміти, щоб використовувати монади в Хаскелл. З практичної точки зору вони просто розумний спосіб пройти навколо "держави" через якусь підземну сантехніку.
starblue

1
Я також хотів би додати цю чудову публікацію в блозі і тут: stephendiehl.com/posts/monads.html Це не відповідає безпосередньо на питання, але, на мій погляд, Стівен виконує чудову роботу, пов’язуючи категорії та монади в Haskell разом. Якщо ви прочитали наведені вище відповіді - це повинно допомогти уніфікувати два способи погляду на це.
Бен Форд

3
Точніше "Для будь-якої категорії С категорія [C, C] його ендофайнерів має моноїдну структуру, індуковану композицією. Моноїдний об'єкт у [C, C] - монада на C." - з en.wikipedia.org/wiki/Monoid_%28category_theory%29. Див. En.wikipedia.org/wiki/Monad_%28category_theory%29 для визначення монади в теорії категорій.

1
@Dmitry функтор є функцією між категоріями, з деякими обмеженнями , щоб бути добре себе. Ендофунктор категорії C - це лише функтор від C до себе. Data.Functor - це клас класів для ендофункторів категорії Hask . Оскільки категорія складається з предметів і морфізмів, функтору потрібно скласти карту обох. Для прикладу f Data.Functor, карта на об'єктах (типи haskell) є f сама, а карта на морфізми (функції haskell) - fmap.
Matthijs

Відповіді:


795

Саме це фразоване слово Джеймса Ірі із його дуже захоплюючої короткої, неповної та переважно неправильної історії мов програмування , в якій він вигадано приписує це Філіпу Вадлеру.

Оригінальна цитата - з Сандер Мак Лейн в категорії «Робочий математик» , один із основоположних текстів Теорії категорій. Ось це в контексті , який, мабуть, найкраще місце, щоб дізнатися, що саме це означає.

Але я візьму колю. Оригінальне речення таке:

Все сказане, монада в X - це просто моноїд у категорії ендофункторів X, причому продукт × замінений складом ендофункторів та одиницею, встановленою ідентифікаційним ендофунктором.

X ось категорія. Endofunctors функтори з категорії в собі (що, як правило , все Functor -е, наскільки функціональні програмісти стурбовані, так як вони в основному має справу тільки з однією категорією; категорія типів - але я відволікся). Але ви можете собі уявити ще одну категорію, яка є категорією "ендофайнерів на X ". Це категорія, в якій об'єкти є ендофаунерами, а морфізми - природними перетвореннями.

І з цих ендофанконів деякі з них можуть бути монадами. Які з них монади? Саме ті, які є моноїдними в певному сенсі. Замість того, щоб конкретизувати точне відображення від монад до моноїдів (оскільки Mac Lane робить це набагато краще, ніж я міг би сподіватися), я просто поставлю їх відповідні визначення поряд і дозволю вам порівняти:

Моноїд - це ...

  • Набір, S
  • Операція, •: S × S → S
  • Елемент S , e: 1 → S

... що відповідають цим законам:

  • (a • b) • c = a • (b • c) , для всіх a , b і c в S
  • e • a = a • e = a , для всіх a в S

Монада - це ...

  • Ендофунктор, T: X → X (у Haskell, тип конструктора типу * -> *з Functorекземпляром)
  • Природне перетворення, μ: T × T → T , де × означає функторний склад ( μ відомий як joinу Haskell)
  • Природне перетворення, η: I → T , де I - тотожний ендофактор на X ( η відомий як returnу Haskell)

... що відповідають цим законам:

  • μ ∘ Tµ = μ ∘ μT
  • μ ∘ Tη = μ ∘ ηT = 1 (тотожність природного перетворення)

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


21
дякую за пояснення та дякую за статтю "Коротка, неповна та здебільшого помилкова історія мов програмування". Я думав, що це може бути звідти. Воістину одна з найвидатніших творів гумору програмування.
Роман А. Тейчер

6
@ Джонатан: У класичній постановці моноїда × означає декартовий добуток множин. Ви можете прочитати більше про це тут: en.wikipedia.org/wiki/Cartesian_product , але основна ідея полягає в тому, що елемент S × T є пара (S, T) , де s ∈ S і T ∈ T . Отже, підпис моноїдного продукту •: S × S -> S у цьому контексті просто означає функцію, яка приймає 2 елементи S як вхід і виробляє інший елемент S як вихід.
Том Крокетт

12
@TahirHassan - У загальній теорії категорій ми маємо справу з непрозорими "об'єктами" замість множин, і тому немає апріорного поняття "елементи". Але якщо ви подумаєте про категорію Set, де об’єкти є наборами, а стрілки - це функції, елементи будь-якого набору S знаходяться у відповідності один до одного з функціями з будь-якого одноелемента, встановленого на S. Тобто для будь-якого елемент e з S , є рівно одна функція f: 1 -> S , де 1 - будь-який одноелементний набір ... (продовження)
Том Крокетт

12
Набори 1-елементних елементів @TahirHassan - це спеціалізація більш загального категоріально-теоретичного поняття "кінцеві об'єкти": термінальний об'єкт - це будь-який об'єкт категорії, для якого є точно одна стрілка від будь-якого іншого об'єкта (ви можете перевірити, що це справедливо для 1-елементних наборів у Set ). У теорії категорій термінальні об'єкти просто називають 1 ; вони унікальні аж до ізоморфізму, тому немає сенсу їх відрізняти. Отже, тепер у нас є чисто категорично-теоретичний опис "елементів S " для будь-якого S : вони просто стрілки від 1 до S !
Том Крокетт

7
@TahirHassan - Якщо висловити це в термінах Haskell, подумайте про те, що якщо Sце тип, все, що ви можете зробити при написанні функції, f :: () -> Sце вибрати якийсь певний тип типу S("елемент", якщо ви хочете) і повернути це ... вам не було надано реальної інформації з аргументом, тому немає можливості змінити поведінку функції. Отже, fповинна бути постійною функцією, яка щоразу повертає те саме. ()("Одиниця") є кінцевим об'єктом категорії Hask , і не випадково існує саме 1 ( нерозбіжне ) значення, яке його населяє.
Том Крокетт

532

Інтуїтивно, я думаю, що те, що говорить фантазійний математичний словник, це:

Моноїд

Моноїд являє собою набір об'єктів, а також спосіб їх об'єднання. Добре відомими моноїдами є:

  • числа, які ви можете додати
  • списки, які можна об'єднати
  • набори, які можна об'єднати

Є і більш складні приклади.

Крім того, у кожного моноїда є ідентичність , яка є тим елементом "без відключення", який не має ефекту, коли ви поєднуєте його з чимось іншим:

  • 0 + 7 == 7 + 0 == 7
  • [] ++ [1,2,3] == [1,2,3] ++ [] == [1,2,3]
  • {} союз {apple} == {apple} союз {} == {яблуко}

Нарешті, моноїд повинен бути асоціативним . (ви можете зменшити довгий рядок комбінацій будь-коли, доки не захочете, якщо ви не зміните ліво-право на порядок об'єктів) Додавання нормально ((5 + 3) +1 == 5+ (3+ 1)), але віднімання не ((5-3) -1! = 5- (3-1)).

Монада

Тепер розглянемо особливий вид набору та особливий спосіб поєднання предметів.

Об'єкти

Припустимо, ваш набір містить об'єкти особливого виду: функції . І ці функції мають цікавий підпис: вони не несуть числа до чисел або рядків до рядків. Натомість кожна функція несе число до списку чисел у двоступеневому процесі.

  1. Обчисліть 0 або більше результатів
  2. Об’єднайте ці результати якось однією відповіддю.

Приклади:

  • 1 -> [1] (просто перегорніть введення)
  • 1 -> [] (відкиньте вхід, загорнуйте нечисть у список)
  • 1 -> [2] (додайте 1 до вводу та заверніть результат)
  • 3 -> [4, 6] (додайте 1 до вводу та помножте введення на 2 та оберніть кілька результатів )

Об'єднання об'єктів

Також наш спосіб поєднання функцій особливий. Простим способом поєднання функції є композиція : Візьмемо наші приклади вище та складемо кожну функцію із себе:

  • 1 -> [1] -> [[1]] (оберніть ввід, двічі)
  • 1 -> [] -> [] (відкиньте введення, загортайте нечистоту в список, двічі)
  • 1 -> [2] -> [UH-OH! ] (ми не можемо "додати 1" до списку! ")
  • 3 -> [4, 6] -> [UH-OH! ] (ми не можемо додати 1 список!)

Не вникаючи занадто багато в теорію типів, справа в тому, що ви можете об'єднати два цілих числа, щоб отримати ціле число, але ви не завжди можете скласти дві функції і отримати функцію одного типу. (Функції з типом a -> a складатимуть, але a-> [a] не буде.)

Отже, давайте визначимо інший спосіб поєднання функцій. Коли ми поєднуємо дві ці функції, ми не хочемо "подвоювати" результати.

Ось що ми робимо. Коли ми хочемо поєднати дві функції F і G, ми слідуємо за цим процесом (званий зв'язуванням ):

  1. Обчисліть "результати" з F, але не комбінуйте їх.
  2. Обчисліть результати від застосування G до кожного з результатів F окремо, отримавши колекцію збірних результатів.
  3. Згладьте дворівневу колекцію та об'єднайте всі результати.

Повернувшись до наших прикладів, давайте поєднаємо (прив’яжемо) функцію до себе за допомогою цього нового способу "прив'язки" функцій:

  • 1 -> [1] -> [1] (оберніть введення, двічі)
  • 1 -> [] -> [] (відкиньте введення, загортайте нечистоту в список, двічі)
  • 1 -> [2] -> [3] (додайте 1, потім знову додайте 1 і заверніть результат.)
  • 3 -> [4,6] -> [5,8,7,12] (додайте 1 на вхід, а також помножте введення на 2, зберігаючи обидва результати, потім зробіть все це знову для обох результатів, а потім заверніть фінал результати в списку.)

Цей більш складний спосіб поєднання функцій є асоціативним (випливає з того, наскільки склад функції асоціативний, коли ви не займаєтесь вигадливими упаковками).

Зв’язуючи все це разом,

  • монада - це структура, яка визначає спосіб поєднання (результатів) функцій,
  • аналогічно тому, як моноїд - це структура, яка визначає спосіб комбінування об'єктів,
  • де метод поєднання асоціативний,
  • і там, де є спеціальний "Не-оп", який може поєднуватися з будь-яким чимось, що призводить до чогось незмінного.

Примітки

Існує маса способів "обернути" результати. Ви можете скласти список, набір або відкинути всі, крім першого результату, зазначаючи, що результатів немає, приєднати кошик стану, надрукувати повідомлення журналу тощо тощо.

Я трохи розгубився з визначеннями, сподіваючись інтуїтивно зрозуміти істотне уявлення.

Я трохи спростив речі, наполягаючи на тому, що наша монада працює над функціями типу a -> [a] . Насправді монади працюють над функціями типу a -> mb , але узагальнення - це вид технічної деталі, який не є основним розумінням.


22
Це приємне пояснення того, як кожна монада становить категорію ( категорія Клейслі - це те, що ви демонструєте - є також категорія Ейленберг-Мур). Але через те, що ви не можете скласти жодної дві стрілки Клейслі a -> [b]і c -> [d](це можна зробити лише якщо b= c), це не зовсім описує моноїд. Це фактично описана вами операція вирівнювання, а не композиція функції, яка є "моноїдним оператором".
Том Крокетт

6
Зрозуміло, якщо ви обмежили монаду лише одним типом, тобто якщо ви дозволили лише стрілки Kleisli форми a -> [a], це буде моноїдом (тому що ви б зменшили категорію Kleisli до одного об'єкта та будь-яку категорію лише одного об'єкта за визначенням є моноїдом!), але він не зафіксував би всю спільність монади.
Том Крокетт

5
Останнє запитання допомагає пам’ятати, що a -> [a] - це просто a -> [] a. ([] теж просто тип конструктора.) І тому його не можна розглядати лише як -> mb, але [] справді є екземпляром класу Monad.
Evi1M4chine

8
Це найкраще і найвибагливіше пояснення монад та їх математичного походження моноїдів, з якими я стикався буквально за тижні. Це те, що має бути надруковано в кожній книзі Хаскелла, коли мова йде про монади, руки вниз. ПОВЕРНУТИСЯ! Можливо, надалі отримайте інформацію, що монади реалізуються як параметризовані екземпляри класового типу, загортаючи все, що міститься в них у haskell, на посаду. (Принаймні, так я їх зрозумів до цього часу. Виправте мене, якщо я помиляюся. Див. Haskell.org/haskellwiki/What_a_Monad_is_not )
sjas

1
Це фантастично - це єдине пояснення, яке я досить добре зрозумів, щоб можна було пояснити це комусь іншому ... Але я все ще не розумію, чому це цінний спосіб думати про що-небудь. :(
Адам Барнс

84

По-перше, розширення та бібліотеки, які ми будемо використовувати:

{-# LANGUAGE RankNTypes, TypeOperators #-}

import Control.Monad (join)

З них RankNTypes- єдиний, який абсолютно важливий для наведеного нижче. Я одного разу написав пояснення, RankNTypesщо деякі люди здаються корисними , тому я посилаюся на це.

Цитуючи відмінну відповідь Тома Кроккета , ми маємо:

Монада - це ...

  • Ендофунктор, T: X -> X
  • Природне перетворення, μ: T × T -> T , де × означає функторний склад
  • Природне перетворення η: I -> T , де I - тотожний ендофактор на X

... що відповідають цим законам:

  • μ (μ (T × T) × T)) = μ (T × μ (T × T))
  • μ (η (T)) = T = μ (T (η))

Як ми переводимо це на код Haskell? Що ж, почнемо з поняття природного перетворення :

-- | A natural transformations between two 'Functor' instances.  Law:
--
-- > fmap f . eta g == eta g . fmap f
--
-- Neat fact: the type system actually guarantees this law.
--
newtype f :-> g =
    Natural { eta :: forall x. f x -> g x }

Тип формі f :-> gаналогічний типу функції, але замість того , щоб думати про нього , як функції між двома типами (у вигляді *), думати про нього , як морфізм між двома функторів (кожен з роду * -> *). Приклади:

listToMaybe :: [] :-> Maybe
listToMaybe = Natural go
    where go [] = Nothing
          go (x:_) = Just x

maybeToList :: Maybe :-> []
maybeToList = Natural go
    where go Nothing = []
          go (Just x) = [x]

reverse' :: [] :-> []
reverse' = Natural reverse

В основному, в Хаскеллі природні перетворення - це функції від якогось типу f xдо іншого типу, g xтакі, що xзмінна типу є "недоступною" для абонента. Так, наприклад, sort :: Ord a => [a] -> [a]не можна перетворитись на природне перетворення, оскільки це "прискіпливо" щодо того, для яких типів ми можемо ініціювати a. Один з інтуїтивно зрозумілих способів, як я часто думаю, це такий:

  • Функтор - це спосіб роботи над вмістом чогось, не торкаючись структури .
  • Природна трансформація - це спосіб діяти над структурою чогось, не торкаючись і не дивлячись на зміст .

Тепер, не виходячи з цього, давайте займемося пунктами визначення.

Перший пункт - "ендофунктор, T: X -> X ". Що ж, кожен Functorз Haskell є ендофанктором у тому, що люди називають "категорією Hask", об'єктами якого є типи Haskell (у своєму роді *) і чиї морфізми є функціями Haskell. Це звучить як складне твердження, але насправді дуже тривіальне. Все, що означає, що a Functor f :: * -> *дає вам засоби побудови типу f a :: *для будь-якої a :: *і функції fmap f :: f a -> f bз будь-якої f :: a -> b, і що вони підкоряються законам функтора.

Другий пункт: Identityфунктор в Haskell (який поставляється з Платформою, тому ви можете просто імпортувати його) визначається таким чином:

newtype Identity a = Identity { runIdentity :: a }

instance Functor Identity where
    fmap f (Identity a) = Identity (f a)

Отже, природне перетворення η: I -> T з визначення Тома Кроккета може бути записане таким чином для будь-якого Monadвипадку t:

return' :: Monad t => Identity :-> t
return' = Natural (return . runIdentity)

Третій пункт: Склад двох функторів у Haskell можна визначити таким чином (що також поставляється з Платформою):

newtype Compose f g a = Compose { getCompose :: f (g a) }

-- | The composition of two 'Functor's is also a 'Functor'.
instance (Functor f, Functor g) => Functor (Compose f g) where
    fmap f (Compose fga) = Compose (fmap (fmap f) fga)

Тож природне перетворення μ: T × T -> T з визначення Тома Крокетта можна записати так:

join' :: Monad t => Compose t t :-> t
join' = Natural (join . getCompose)

Твердження, що це моноїд у категорії ендофункторів, означає, що Compose(частково застосовано лише до перших двох його параметрів) є асоціативним, і Identityце його елемент ідентичності. Тобто, мають місце такі ізоморфізми:

  • Compose f (Compose g h) ~= Compose (Compose f g) h
  • Compose f Identity ~= f
  • Compose Identity g ~= g

Це дуже легко довести , бо Composeі Identityобидва визначені як newtype, і Haskell звіти визначають семантику newtypeяк ізоморфізм між типом визначається і тип аргументу newtypeконструктора «и даних. Так, наприклад, докажемо Compose f Identity ~= f:

Compose f Identity a
    ~= f (Identity a)                 -- newtype Compose f g a = Compose (f (g a))
    ~= f a                            -- newtype Identity a = Identity a
Q.E.D.

У Naturalновотипі я не можу зрозуміти, що (Functor f, Functor g)створює обмеження. Чи можете ви пояснити?
dfeuer

@dfeuer Це насправді не робить нічого істотного.
Луїс Касільяс

1
@LuisCasillas Я усунув ці Functorобмеження, оскільки вони не здаються потрібними. Якщо ви не згодні, тоді не соромтесь додати їх назад.
Фея лямбда

Чи можете ви детальніше зупинитися на тому, що офіційно означає, що твір функторів слід приймати як композицію? Зокрема, які проекційні морфізми для композиції функтора? Я здогадуюсь, що добуток визначений лише для функтора F проти себе, F x F і лише тоді, коли joinвизначено. І joinце проекційний морфізм. Але я не впевнений.
tksfz

6

Примітка: Ні, це неправда. У якийсь момент був коментар цієї відповіді від самого Дана Піпоні, який сказав, що причина і наслідок тут були прямо протилежні, що він написав свою статтю у відповідь на запит Джеймса Ірі. Але, здається, його зняли, можливо, якийсь нав'язливий приналежність.

Нижче моя оригінальна відповідь.


Цілком можливо, що Ірі прочитала « Моноїди до монадів» , посаду, в якій Дан Піпоні (сигфпе) виводить монади з моноїдів у Хаскеллі, багато обговорюючи теорію категорій і чітко згадуючи про «категорію ендофунторів на Хаскі ». У будь-якому випадку, кожен, хто замислиться про те, що означає монада бути моноїдом у категорії ендофайнерів, може отримати користь від того, щоб прочитати це виведення.


1
"Можливо, за допомогою якогось компульсивного" - або, як ми з любов'ю називаємо їх на цьому сайті, модератора :-).
півзахисник

6

Я прийшов на цю посаду шляхом кращого розуміння висновку сумнозвісної цитати з Теорії категорій Мака Лейн для працюючого математика .

Описуючи, що щось таке, часто так само корисно описувати те, що це не так.

Той факт, що Mac Lane використовує опис для опису монади, може означати, що він описує щось унікальне для монад. Ведміть мене. Щоб розвинути більш широке розуміння твердження, я вважаю, що йому слід чітко пояснити, що він не є описує те, що є унікальним для монад; у заяві однаково описуються додаткові та стрілки серед інших. З тієї ж причини ми можемо мати два моноїди на Int (сума та продукт), у нас може бути декілька моноїдів на X у категорії ендофункторів. Але подібності є ще більше.

І Monad, і Applicative відповідають критеріям:

  • endo => будь-яка стрілка, або морфізм, який починається і закінчується там же
  • functor => будь-яка стрілка або морфізм між двома категоріями

    (наприклад, день у день Tree a -> List b, але в категорії Tree -> List)

  • моноїд => одиничний об'єкт; тобто, одного типу, але в цьому контексті лише стосовно зовнішнього шару; так що, ми не можемо мати Tree -> Listтільки List -> List.

У операторі використовується "Категорія ...". Це визначає сферу твердження. Як приклад, категорія Functor описує сферу дії f * -> g *, тобто Any functor -> Any functor, наприклад, Tree * -> List *абоTree * -> Tree * .

Що категоричне твердження не вказує, описує, де все і все дозволено .

У цьому випадку всередині функторів * -> *ака a -> bне вказується, що означає Anything -> Anything including Anything else. Коли моя фантазія стрибає до Int -> String, вона також включає Integer -> Maybe Intабо навіть Maybe Double -> Either String Intкуди a :: Maybe Double; b :: Either String Int.

Отже, твердження складається разом так:

  • область функтора :: f a -> g b(тобто будь-який параметризований тип до будь-якого параметру)
  • ендо + функтор :: f a -> f b(тобто будь-який один параметризований тип до того ж параметризованого типу) ... сказано по-різному,
  • моноїд у категорії ендофункторів

Отже, звідки сила цієї конструкції? Щоб оцінити повну динаміку, мені потрібно було зрозуміти, що типові малюнки моноїда (єдиного об’єкта з тим, що схоже на стрілку ідентичності, :: single object -> single object) не ілюструють, що мені дозволяється використовувати стрілку, параметризовану на будь-яку кількість моноїдних значень, від об'єкта одного типу, дозволеного в Monoid. Визначення еквівалентності стрілки endo, ~ ідентичності ігнорує значення типу функтора, а також тип і значення самого внутрішнього шару "корисного навантаження". Таким чином, еквівалентність повертається trueв будь-якій ситуації, коли функціональні типи збігаються (наприклад, Nothing -> Just * -> Nothingеквівалентні Just * -> Just * -> Just *тому, що вони обидва Maybe -> Maybe -> Maybe).

Бічна панель: ~ зовні є концептуальною, але є найбільш лівим символом у f a. Він також описує, що читає "Haskell" спочатку (велика картина); тож тип "зовні" стосовно значення типу. Взаємозв'язок між шарами (ланцюжком посилань) у програмуванні непросто пов'язати у категорії. Категорія набору використовується для опису типів (Int, Strings, можливо Int тощо), що включає категорію Functor (параметризовані типи). Опорний ланцюг: Тип функціонера, Значення функціоналу (елементи набору цього функтора, наприклад, Нічого, Просто), і, в свою чергу, все інше, на яке вказує значення кожного функтора. У категорії відносини описуються по-різному, наприклад, return :: a -> m aвважається природним перетворенням від одного Функтора до Іншого Функтора, відмінним від усього, що згадувалося до цього часу.

Повертаючись до основної нитки, в цілому для будь-якого визначеного тензорного добутку та нейтрального значення, заява закінчується описом дивовижно потужної обчислювальної конструкції, народженої з його парадоксальної структури:

  • зовні він постає як єдиний об'єкт (наприклад, :: List); статичний
  • але всередині, дозволяє багато динаміки
    • будь-яка кількість значень того ж типу (наприклад, Empty | ~ NonEmpty), що є кормом для функцій будь-якої арії. Продукт тензора зменшить будь-яку кількість входів до одного значення ... для зовнішнього шару (~ foldщо нічого не говорить про корисне навантаження)
    • нескінченний діапазон як типу, так і значень для самого внутрішнього шару

У Хаскеллі важливим є уточнення застосовності твердження. Потужність і універсальність цієї конструкції, не має абсолютно нічого спільного з монадой по собі . Іншими словами, конструкція не покладається на те, що робить монаду унікальною.

Намагаючись розібратися, чи потрібно будувати код із спільним контекстом для підтримки обчислень, які залежать один від одного, порівняно з обчисленнями, які можна виконувати паралельно, це сумнозвісне твердження, настільки, наскільки воно описує, не є контрастом між вибором Додаток, стрілки та монади, а це опис того, наскільки вони однакові. Що стосується рішення, заява суперечлива.

Це часто неправильно розуміється. Твердження далі описується join :: m (m a) -> m aяк тензорний добуток для моноїдного ендофунктора. Однак це не чітко визначає, як у контексті цього твердження (<*>)також можна було вибрати. Це справді є прикладом шести / півдюжини. Логіка поєднання значень точно однакова; один і той же вхід генерує однаковий вихід з кожного (на відміну від моноїдів Sum та Product для Int, оскільки вони створюють різні результати при поєднанні Ints).

Отже, підводячи підсумки: Моноїд у категорії ендофайнерів описує:

   ~t :: m * -> m * -> m *
   and a neutral value for m *

(<*>)і (>>=)обидва забезпечують одночасний доступ до двох mзначень, щоб обчислити єдине повернене значення. Логіка, яка використовується для обчислення повернутого значення, точно така ж. Якби не різні форми функцій, які вони параметризують ( f :: a -> bпроти k :: a -> m b) та положення параметра з тим самим типом повернення обчислення (тобто a -> b -> bпроти b -> a -> bкожної відповідно), я підозрюю, що ми могли б параметризувати моноїдну логіку, тензорний добуток для повторного використання в обох визначеннях. Як вправу зробити точку, спробуйте виконати ~t, і ви закінчите (<*>)і (>>=)залежно від того, як ви вирішите це визначити forall a b.

Якщо мій останній пункт є як мінімум концептуально правдивим, то він пояснює точну та єдину обчислювальну різницю між додатком та монадою: функції, які вони параметризують. Іншими словами, різниця є зовнішньою для реалізації класів цих типів.

На закінчення, на моєму власному досвіді, сумнозвісна цитата Мак-Лейн забезпечила чудовий мемо «гото», який я можу посилатись, перебуваючи в дорозі по категорії, щоб краще зрозуміти ідіоми, які використовуються в Haskell. Йому вдалося захопити сферу потужної обчислювальної спроможності, яка стала чудово доступною в Haskell.

Однак є іронія в тому, як я вперше неправильно зрозумів застосовність твердження поза монадою, і те, що я сподіваюсь, передано тут. Все, що він описує, виявляється подібним між Applicative та Monads (і Стрілками серед інших). Те, що вона не говорить, - це саме невелика, але корисна відмінність між ними.

- Е


5

Відповіді тут відмінно справляються із визначенням моноїдів та монад, однак, вони все ще не відповідають на питання:

І на менш важливу увагу, чи це правда, і якщо так, ви можете дати пояснення (сподіваємось, таке, яке може зрозуміти той, хто не має багато досвіду Haskell)?

Суть речовини, якої тут бракує, полягає в різному понятті «моноїд», так званій категоризації, точніше - моноїді в моноїдальній категорії. На жаль, книга Мака Лейна сама робить це дуже заплутаним :

За всіма словами, монада в X- це просто моноїд у категорії ендофункторів X, при цьому продукт ×замінюється складом ендофункторів та блоком, встановленим ідентифікаційним ендофунктором.

Основна плутанина

Чому це заплутано? Тому що це не визначає, що таке "моноїд у категорії ендофайнерів" X. Натомість це речення пропонує взяти моноїд всередині набору всіх ендофайнерів разом із композицією функтора як бінарну операцію та функціонер ідентичності як моноїдну одиницю. Це прекрасно працює і перетворює в моноїд будь-яку підмножину ендофункторів, що містить функцію ідентифікатора і закрита під функторною композицією.

І все-таки це не правильне тлумачення, яке книга не дає зрозуміти на цьому етапі. Монада f- це нерухомий ендофунктор, а не підмножина ендофункторів, закритих під композицію. Загальна будівництво повинні використовувати fдля генерації моноїд, приймаючи безліч всіх k-кратноє композицій f^k = f(f(...))з fз самими собою, в тому числі , k=0що відповідає ідентичності f^0 = id. І тепер сукупність Sусіх цих повноважень для всіх k>=0справді є моноїдом "з продуктом × заміненим складом ендофункторів та одиницею, встановленою ідентифікаційним ендофунктором".

І ще:

  • Цей моноїд Sможна визначити для будь-якого функтора fабо навіть буквально для будь- якої самодозвілля X. Це моноїд, породжений f.
  • Моноїдна структура, Sзадана композицією функтора та ідентифікатором функтора, не має нічого спільного з fтим, щоб бути монадою чи бути нею.

А щоб зробити речі більш заплутаними, визначення "моноїд у моноїдальній категорії" з’являється пізніше в книзі, як ви можете бачити зі змісту . І все ж розуміння цього поняття є абсолютно критичним для розуміння зв'язку з монадами.

(Суворі) моноїдні категорії

Переходячи до глави VII на моноїд (який приходить пізніше , ніж глава VI на монадах), ми знаходимо визначення так званої суворої категорії моноідальной як потрійні (B, *, e), де Bкатегорію, *: B x B-> Bв біфунктора (функтор по відношенню до кожного компоненту з іншим компонентом фіксованого ) і eє одиничним об'єктом B, що відповідає законам асоціативності та одиниці:

(a * b) * c = a * (b * c)
a * e = e * a = a

для будь-яких об'єктів a,b,cз B, і того ж тотожності для будь-яких морфізма a,b,cз eзамінено id_e, тотожним морфізма e. Зараз повчальним Bє зауваження, що в нашому випадку, де знаходиться категорія ендофанкторів Xіз природними перетвореннями, як морфізми, *композиція функтора та функція eідентичності, всі ці закони задовольняються, як це можна безпосередньо перевірити.

У книзі йдеться про визначення «розслабленої» моноїдальної категорії , де закони містять лише модулі певних фіксованих природних перетворень, що задовольняють так звані когерентні відносини , що, однак, не важливо для наших випадків категорій ендофактора.

Моноїди в моноїдних категоріях

Нарешті, у розділі 3 "Моноїди" глави VII подано фактичне визначення:

Моноїд cв моноїдній категорії (B, *, e)- це об'єкт Bз двома стрілками (морфізми)

mu: c * c -> c
nu: e -> c

роблячи 3 діаграми комутативними. Нагадаємо, що в нашому випадку це морфізми в категорії ендофаніків, які є природними перетвореннями, що відповідають точно joinі returnдля монади. Зв'язок стає ще більш очевидним , коли ми робимо складу *більш явним, замінивши c * cна c^2, де cнаша монада.

Нарешті, зауважте, що 3 комутативні діаграми (у визначенні моноїд у моноїдальній категорії) написані для загальних (не строгих) моноїдних категорій, тоді як у нашому випадку всі природні перетворення, що виникають у складі моноїдної категорії, є насправді тотожностями. Це зробить діаграми абсолютно такими ж, як і у визначенні монади, зробивши кореспонденцію повною.

Висновок

Підсумовуючи це, будь-яка монада за визначенням є ендофунктором, отже, об'єктом у категорії ендофункторів, де монадики joinта returnоператори задовольняють визначенню моноїду у цій конкретній (суворій) моноїдальній категорії . І навпаки, будь-який моноїд у моноїдній категорії ендофайнерів за визначенням є потрійним, (c, mu, nu)що складається з предмета та двох стріл, наприклад, природних перетворень у нашому випадку, що відповідають тим же законам, що й монада.

Нарешті, зазначимо ключову різницю між (класичними) моноїдами та більш загальними моноїдами в моноїдних категоріях. Дві стрілки muі nuвище вже не є бінарною операцією та одиницею в наборі. Натомість у вас є один фіксований ендофактор c. Композиція функтора *та сам функціонер ідентичності не забезпечують повну структуру, необхідну монаді, незважаючи на це заплутане зауваження в книзі.

Інший підхід був би порівняти зі стандартним моноїд Cвсіх самостійних відображень безлічі A, де бінарна операція є композиція, яка може бути помічена , щоб відобразити стандартний декартовій твір C x Cв C. Переходячи до категоризованого моноїда, ми заміняємо декартовий продукт xна композицію функтора *, і двійкова операція замінюється природним перетворенням muз c * cв c, тобто сукупність joinоператорів

join: c(c(T))->c(T)

для кожного об'єкта T(тип програмування). А елементи ідентичності в класичних моноїдах, які можна ідентифікувати із зображеннями карт із фіксованого одноточкового набору, замінюють колекцією returnоператорів

return: T->c(T) 

Але зараз більше немає декартових продуктів, тому немає пар елементів і, отже, немає двійкових операцій.


Отже, яка ваша відповідь на "це правдива" частина питання? Чи правда, що монада - це моноїд у категорії ендофаніків? І якщо так, то який взаємозв'язок між поняттям теорії категорій моноїд та алгебраїчним моноїдом (множиною з асоціативним множенням та одиницею)?
Олександр Білопольський
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.