it-swarm.com.ru

Что такое бесплатные монады?

Я видел термин Free Monad всплывающее каждыйсейчаситогда в течение некоторого времени, но все, кажется, просто используют/обсуждают их, не объясняя, что они собой представляют. Итак: что такое бесплатные монады? (Я бы сказал, что я знаком с монадами и основами Хаскелла, но имею только очень грубые знания теории категорий.)

343
David

Ответ Эдварда Кметта, очевидно, великолепен. Но это немного технический. Вот, пожалуй, более доступное объяснение.

Свободные монады - это просто общий способ превращения функторов в монады. То есть, если любой функтор fFree f является монадой. Это было бы не очень полезно, если бы вы не получили пару функций

liftFree :: Functor f => f a -> Free f a
foldFree :: Functor f => (f r -> r) -> Free f r -> r

первый из них позволяет вам "войти" в вашу монаду, а второй - как "выйти" из нее.

В более общем смысле, если X - это Y с некоторыми дополнительными элементами P, то "свободный X" - это способ перехода от Y к X без получения чего-либо дополнительного.

Примеры: моноид (X) - это набор (Y) с дополнительной структурой (P), который в основном говорит, что у него есть операция (вы можете думать о сложении) и некоторая идентичность (например, ноль).

Так

class Monoid m where
   mempty  :: m
   mappend :: m -> m -> m

Теперь мы все знаем списки

data [a] = [] | a : [a]

Ну, учитывая любой тип t мы знаем, что [t] является моноидом

instance Monoid [t] where
  mempty   = []
  mappend = (++)

и поэтому списки являются "свободным моноидом" над множествами (или в типах Haskell).

Итак, бесплатные монады - это та же идея. Мы берем функтор и возвращаем монаду. На самом деле, поскольку монады можно рассматривать как моноиды в категории эндофункторов, определение списка

data [a] = [] | a : [a]

очень похоже на определение свободных монад

data Free f a = Pure a | Roll (f (Free f a))

и экземпляр Monad имеет сходство с экземпляром Monoid для списков

--it needs to be a functor
instance Functor f => Functor (Free f) where
  fmap f (Pure a) = Pure (f a)
  fmap f (Roll x) = Roll (fmap (fmap f) x)

--this is the same thing as (++) basically
concatFree :: Functor f => Free f (Free f a) -> Free f a
concatFree (Pure x) = x
concatFree (Roll y) = Roll (fmap concatFree y)

instance Functor f => Monad (Free f) where
  return = Pure -- just like []
  x >>= f = concatFree (fmap f x)  --this is the standard concatMap definition of bind

теперь мы получаем две наши операции

-- this is essentially the same as \x -> [x]
liftFree :: Functor f => f a -> Free f a
liftFree x = Roll (fmap Pure x)

-- this is essentially the same as folding a list
foldFree :: Functor f => (f r -> r) -> Free f r -> r
foldFree _ (Pure a) = a
foldFree f (Roll x) = f (fmap (foldFree f) x)
270
Philip JF

Вот еще более простой ответ: Monad - это то, что "вычисляет", когда монадический контекст свернут join :: m (m a) -> m a (напоминая, что >>= можно определить как x >>= y = join (fmap y x)). Вот как монады переносят контекст через последовательную цепочку вычислений: потому что в каждой точке ряда контекст предыдущего вызова свернут со следующим.

A свободная монада удовлетворяет всем законам монады, но не делает никаких коллапсов (то есть вычислений). Он просто создает вложенную серию контекстов. Пользователь, который создает такое бесплатное монадическое значение, отвечает за то, чтобы что-то делать с этими вложенными контекстами, так что значение такой композиции может быть отложено до монадическое значение было создано.

376
John Wiegley

Случайно, что бесплатный foo - это самая простая вещь, которая удовлетворяет всем законам "foo". То есть он полностью соответствует законам, необходимым для того, чтобы быть обманщиком, и ничего лишнего.

Забывчивый функтор - это тот, кто "забывает" часть структуры при переходе из одной категории в другую.

Для заданных функторов F : D -> C и G : C -> D, мы говорим, что F -| G, F присоединяется слева к G, или G присоединяется справа к F всякий раз, когда a, b: F a -> b изоморфно a -> G b, откуда идут соответствующие категории.

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

Свободный моноид

Давайте начнем с более простого примера - свободного моноида.

Возьмем моноид, который определяется некоторым набором носителей T, двоичной функцией, которая объединяет пару элементов вместе f :: T → T → T и unit :: T, так что у вас есть ассоциативный закон и закон тождества: f(unit,x) = x = f(x,unit).

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

Затем вы можете определить функтор F из категории наборов обратно в категорию моноидов, которая остается присоединенной к этому функтору. Этот функтор является функтором, который отображает набор a в моноид [a], где unit = [] и mappend = (++).

Итак, чтобы рассмотреть наш пример в псевдо-Haskell:

U : Mon → Set -- is our forgetful functor
U (a,mappend,mempty) = a

F : Set → Mon -- is our free functor
F a = ([a],(++),[])

Затем, чтобы показать, что F свободен, нужно продемонстрировать, что он оставлен присоединенным к U, функтору забвения, то есть, как мы упоминали выше, нам нужно показать, что

F a → b изоморфен a → U b

помните, что цель F находится в категории Mon моноидов, где стрелки - моноидные гомоморфизмы, поэтому нам нужно показать, что моноидный гомоморфизм из [a] → b может быть точно описан функцией из a → b.

В Haskell мы называем сторону этого, которая живет в Set (er, Hask, категория типов Haskell, которую мы притворяемся, это Set), просто foldMap, которая, когда специализируется от Data.Foldable к Lists, имеет тип Monoid m => (a → m) → [a] → m.

Из этого следствия вытекают последствия. Примечательно, что если вы забудете, то создайте бесплатно, а затем снова забудете, это так же, как вы однажды забыли, и мы можем использовать это для создания монадического соединения. поскольку UFUF ~ U(FUF) ~ UF, и мы можем передать гомоморфизм единичного моноида из [a] в [a] через изоморфизм, который определяет наше присоединение, получаем, что изоморфизм списков из [a] → [a] является функцией типа a -> [a], и это просто возврат для списков.

Вы можете составить все это более непосредственно, описав список в следующих терминах:

newtype List a = List (forall b. Monoid b => (a -> b) -> b)

Свободная Монада

Так что же такое бесплатная монада ?

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

Итак, как это относится к понятию свободной монады, как это обычно используется?

Знание того, что что-то является свободной монадой, Free f, говорит вам, что дать гомоморфизм монад из Free f -> m - это то же самое (изоморфно), что и естественное преобразование (гомоморфизм функторов) из f -> m. Помните, что F a -> b должен быть изоморфен a -> U b, чтобы F оставался присоединенным к U. U здесь отображает монады в функторы.

F по крайней мере изоморфен типу Free, который я использую в своем пакете free при взломе.

Мы могли бы также построить его в более тесной аналогии с приведенным выше кодом для свободного списка, определив

class Algebra f x where
  phi :: f x -> x

newtype Free f a = Free (forall x. Algebra f x => (a -> x) -> x)

Cofree Comonads

Мы можем построить нечто подобное, взглянув на правое сопряжение с забывчивым функтором, предполагая, что оно существует. Свободный функтор просто/прямо присоединен/к забывчивому функтору, и симметрично знать, что что-то является свободной комонадой, - это то же самое, что знать, что задание гомоморфизма комонады из w -> Cofree f - это то же самое, что дать естественное преобразование из w -> f.

132
Edward KMETT

Free Monad (структура данных) относится к Monad (классу), как List (структура данных) к Monoid (классу): это тривиальная реализация, в которой вы можете впоследствии решить, как будет комбинироваться контент.


Вы, вероятно, знаете, что такое Монада, и что каждая Монада нуждается в конкретной (монадной законопослушной) реализации либо fmap + join + return, либо bind + return.

Предположим, у вас есть Functor (реализация fmap), но все остальное зависит от значений и выборов, сделанных во время выполнения, что означает, что вы хотите иметь возможность использовать свойства Monad, но затем хотите выбрать функции Monad.

Это можно сделать с помощью Free Monad (структура данных), которая оборачивает Functor (тип) таким образом, что join является скорее суммированием этих функторов, чем сокращением.

Реальные return и join, которые вы хотите использовать, теперь могут быть заданы в качестве параметров для функции сокращения foldFree :

foldFree :: Functor f => (a -> b) -> (f b -> b) -> Free f a -> b
foldFree return join :: Monad m => Free m a -> m a

Чтобы объяснить типы, мы можем заменить Functor f на Monad m и b на (m a):

foldFree :: Monad m => (a -> (m a)) -> (m (m a) -> (m a)) -> Free m a -> (m a)
61
comonad

Свободная монада Haskell - это список функторов. Для сравнения:

data List a   = Nil    | Cons  a (List a  )

data Free f r = Pure r | Free (f (Free f r))

Pure аналогично Nil, а Free аналогично Cons. Бесплатная монада хранит список функторов вместо списка значений. Технически, вы можете реализовать свободные монады, используя другой тип данных, но любая реализация должна быть изоморфна вышеупомянутой.

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

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

55
Gabriel Gonzalez

Я думаю, что простой конкретный пример поможет. Предположим, у нас есть функтор

data F a = One a | Two a a | Two' a a | Three Int a a a

с очевидным fmap. Тогда Free F a - это тип деревьев, листья которых имеют тип a, а чьи узлы помечены One, Two, Two' и Three. One- узлы имеют один дочерний элемент, Two- и Two'- узлы имеют два дочерних элемента, а Three- узлы имеют три дочерних элемента и также помечены Int.

Free F - это монада. return отображает x в дерево, которое является просто листом со значением x. t >>= f просматривает каждое из листьев и заменяет их деревьями. Когда лист имеет значение y, он заменяет этот лист на дерево f y.

Диаграмма делает это более понятным, но у меня нет возможности легко ее нарисовать!

21
Tom Ellis