Простые примеры для иллюстрации категории, моноида и монады?
Я очень путаюсь с этими тремя понятиями.
есть ли простые примеры, чтобы проиллюстрировать различия между Категория, моноид и Монада ?
было бы очень полезно, если есть иллюстрации этих абстрактных понятий.
1 ответов
это не ответ, который вы ищете, но здесь вы идете в любом случае:
действительно кривой способ смотреть на монады & co.
один из способов взглянуть на абстрактные понятия, как это, чтобы связать их с основными понятиями, такими как обычные операции обработки списка. Тогда, можно сказать, что
- категория обобщает
(.)
операции. - моноид обобщает
(++)
операции. - A функтор обобщает
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
сравните это с определением нормальных .
Прикладной Функтор
аппликативные функторы можно рассматривать как вещи с генерализованной zipWith
операции. Функторы отображают общие структуры по одному в то время, но с помощью прикладного функтора вы можете zip вместе две или более структур. Для простейшего примера вы можете использовать аппликаторы для zip вместе два целых числа внутри 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
и (<*>)
.
Монаду
монады часто используется для моделирования различных вычислительных контекстов, таких как недетерминированные, или effectful вычислений. Поскольку уже слишком много учебников монады, я просто рекомендую лучший, вместо того, чтобы писать еще один.
что касается обычных функций обработки списка, монады обобщают функцию concat :: [[a]] -> [a]
работать с много других видов структур кроме списков. В качестве простого примера, монадическая операция join
может использоваться для сглаживания вложенных Maybe
значения:
join (Just (Just 42)) -- gives Just 42
join (Just (Nothing)) -- gives Nothing
как это связано с использованием монад в качестве средства структурирования вычислений? Рассмотрим пример игрушки, где вы делаете два последовательных запроса из некоторой базы данных. Первый запрос возвращает вам некоторое значение ключа, с которым вы хотите выполнить другой поиск. Проблема здесь в том, что первое значение обернуто внутри Maybe
, поэтому вы не можете запросить с этим непосредственно. Вместо этого, как может быть Functor
, вы могли бы вместо fmap
возвращаемое значение с новым запросом. Это даст вам два вложенных Maybe
значения, как указано выше. Другой запрос приведет к трем уровням Maybe
s. Это было бы довольно сложно запрограммировать, но монадический join
дает вам возможность сгладить эту структуру и работать только с одним уровнем Maybe
s.
(я думаю, что буду редактировать этот пост много, прежде чем это будет иметь смысл..)