Простые примеры для иллюстрации категории, моноида и монады?

Я очень запутался в этих трех концепциях.

Есть ли какие-нибудь простые примеры, иллюстрирующие различия между Category, Monoid и Monad?

Было бы очень полезно, если бы была иллюстрация этих абстрактных понятий.


person Znatz    schedule 31.03.2013    source источник
comment
Вы читали Typeclassopedia?   -  person dave4420    schedule 31.03.2013
comment
Спасибо за ваш совет. Я должен был сначала прочитать это.   -  person Znatz    schedule 01.04.2013
comment
Монады похожи на буррито. Действительно.   -  person Will Ness    schedule 01.04.2013


Ответы (2)


Вероятно, это не тот ответ, который вы ищете, но все равно можете:

Действительно искаженный взгляд на монады и компанию.

Один из способов взглянуть на подобные абстрактные концепции - связать их с базовыми концепциями, такими как обычные операции обработки списков. Тогда вы могли бы сказать это,

  • Категория обобщает операцию (.).
  • Моноид обобщает операцию (++).
  • Функтор обобщает операцию map.
  • Аппликативный функтор обобщает операцию zip (или zipWith).
  • Монада обобщает операцию concat.

Категория

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

В Haskell это моделируется как класс типов, который представляет категорию типов Haskell как объектов.

 class Category cat where
  id :: cat a a
  (.) :: cat b c -> cat a b -> cat a c

Базовыми примерами категории являются функции. Каждая функция связывает два типа, для всех типов существует функция id :: a -> a, которая связывает тип (и значение) с самим собой. Композиция функций - это обычная композиция функций.

Короче говоря, категории в базе Haskell - это вещи, которые ведут себя как функции, т.е. вы можете размещать одну за другой с помощью обобщенной версии (.).

Моноид

Моноид - это набор с единичным элементом и ассоциативной операцией. В Haskell это моделируется как:

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

Общие примеры моноидов включают:

  • набор целых чисел, элемент 0 и операция (+).
  • набор натуральных чисел, элемент 1 и операция (*).
  • набор всех списков, пустой список [] и операция (++).

Они моделируются в Haskell как

newtype Sum a = Sum {getSum :: a}
instance (Num a) => Monoid (Sum a) where
  mempty  = Sum 0
  mappend (Sum a) (Sum b) = Sum (a + b)  

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

Моноиды используются для «объединения» и накопления вещей. Например, функцию mconcat :: Monoid a => [a] -> a можно использовать для сокращения списка сумм до единой суммы или вложенного списка в плоский список. Считайте это своего рода обобщением (++) или (+) операций, которые в некотором роде «объединяют» две вещи.

Функтор

Функтор в Haskell - это вещь, которая напрямую обобщает операцию map :: (a->b) -> [a] -> [b]. Вместо отображения списка он отображает некоторую структуру, например список, двоичное дерево или даже операцию ввода-вывода. Функторы моделируются следующим образом:

class Functor f where
  fmap :: (a->b) -> f a -> f b

Сравните это с определением нормальной map функции.

Аппликативный функтор

Аппликативные функторы можно рассматривать как объекты с обобщенной zipWith операцией. Функторы сопоставляются с общими структурами по одной, но с помощью функтора Applicative вы можете соединить вместе две или более структур. В простейшем примере вы можете использовать аппликативы для соединения двух целых чисел внутри типа Maybe:

pure (+) <*> Just 1 <*> Just 2  -- gives Just 3

Обратите внимание, что структура может повлиять на результат, например:

pure (+) <*> Nothing <*> Just 2  -- gives Nothing

Сравните это с обычной функцией zipWith:

zipWith (+) [1] [2]  

Аппликатив работает не только со списками, но и со всеми видами структур. Вдобавок хитрый трюк с pure и (<*>) обобщает сжатие для работы с любым количеством аргументов. Чтобы увидеть, как это работает, изучите следующие типы, сохраняя при этом понятие частично применяемых функций:

instance (Functor f) => Applicative f where
  pure  :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b

Обратите также внимание на сходство между fmap и (<*>).

Монада

Монады часто используются для моделирования различных вычислительных контекстов, таких как недетерминированные вычисления или вычисления с побочными эффектами. Поскольку уже существует слишком много руководств по монадам, я просто рекомендую Лучшее один вместо того, чтобы писать еще один.

Что касается обычных функций обработки списков, монады обобщают функцию concat :: [[a]] -> [a] для работы со многими другими видами структур помимо списков. В качестве простого примера можно использовать монадическую операцию join для сглаживания вложенных Maybe значений:

join (Just (Just 42)) -- gives Just 42
join (Just (Nothing)) -- gives Nothing

Как это связано с использованием монад как средства структурирования вычислений? Рассмотрим игрушечный пример, в котором вы выполняете два последовательных запроса из некоторой базы данных. Первый запрос возвращает вам какое-то значение ключа, с которым вы хотите выполнить еще один поиск. Проблема здесь в том, что первое значение заключено в Maybe, поэтому вы не можете запрашивать его напрямую. Вместо этого, поскольку это, возможно, Functor, вы могли бы вместо этого fmap возвращать значение с новым запросом. Это даст вам два вложенных Maybe значения, как указано выше. Другой запрос приведет к трем уровням Maybe. Это было бы довольно сложно запрограммировать, но монадический join дает вам способ сгладить эту структуру и работать только с одним уровнем Maybe.

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

person aleator    schedule 31.03.2013
comment
mempty = 0 должно быть mempty = Sum 0. - person snak; 01.04.2013
comment
Мне нравится этот ответ, но вы, возможно, захотите указать, что стандартный экземпляр Applicative для [] - это не динамичный, а декартово произведение. Не очень полезная монада динамичных списков действительна только для списков постоянной длины, а ее join занимает диагональ. - person C. A. McCann; 01.04.2013
comment
Аппликативные функторы @ C.A.McCann динамичны в другом смысле, не так ли, в смысле наличия метода fzip :: (f a, f b) -> f (a,b), объединяющего два f в один. (см. stackoverflow.com/a/15211856/849891). - person Will Ness; 01.04.2013
comment
@WillNess: Обычно фраза «резкий Applicative» означает экземпляр, который ведет себя как структурное пересечение, сопоставляя элементы из двух структур, которые имеют одинаковую позицию. Такой fzip, реализованный с Applicative, может не вести себя как zip с другими экземплярами, очевидным примером является экземпляр списка по умолчанию, где fzip даст полное декартово произведение. - person C. A. McCann; 01.04.2013
comment
@ C.A.McCann да, по всей видимости, я пропустил слово также там, поскольку в are также "zippy" в другом смысле .... - person Will Ness; 02.04.2013

Я думаю, что для понимания монад нужно поиграться с оператором связывания (>>=). Сильно под влиянием [http://dev.stephendiehl.com/hask/#eightfold-path-to-monad-satori](Don читайте учебники по монаде.)

Моя маленькая пьеса такова:

1. Объедините getLine и putStrLn

По материалам http://www.haskellforall.com/2014/10/how-to-desugar-haskell-code.html

Prelude> f = getLine >>= \a -> putStrLn a
Prelude> f
abc
abc
Prelude>

и подписи:

Prelude> :t getLine
getLine :: IO String
Prelude> :t (\a -> putStrLn a)
(\a -> putStrLn a) :: String -> IO ()
Prelude> :t f
f :: IO ()

Результат: видны части подписи (>>=) :: Monad m => m a -> (a -> m b) -> m b.

2. concat Может быть

Адаптация из https://wiki.haskell.org/Simple_monad_examples

Prelude> g x = if (x == 0) then Nothing else Just (x + 1)
Prelude> Just 0 >>= g
Nothing
Prelude> Just 1 >>= g
Just 2

Результат: fail "zero" is Nothing

3. Думайте о связывании как о соединении вычислений.

... как описано в https://www.slideshare.net/ScottWlaschin/functional-design-patterns-devternity2018

введите описание изображения здесь

person Evgeny    schedule 17.05.2019