Я очень смущен этими тремя понятиями.
Есть ли простые примеры для иллюстрации различий между Категория, моноиды и монады?
Было бы очень полезно, если бы была иллюстрация этих абстрактных понятий.
Я очень смущен этими тремя понятиями.
Есть ли простые примеры для иллюстрации различий между Категория, моноиды и монады?
Было бы очень полезно, если бы была иллюстрация этих абстрактных понятий.
Это, вероятно, не тот ответ, который вы ищете, но здесь вы все равно:
Один из способов взглянуть на абстрактные понятия, подобные этим, - связать их с базовыми понятиями, такими как обычные операции обработки списка. Тогда вы могли бы сказать, что
(.)
.(++)
.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
Общие примеры моноидов включают:
(+)
.(*)
.[]
и операция (++)
.Они моделируются в 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
. Функторы отображают по общим структурам по одному в то время, но с аппликативным функтором вы можете объединить две или несколько структур. Для простейшего примера вы можете использовать аппликаторы для zip вместе целых чисел внутри типа Maybe
:
pure (+) <*> Just 1 <*> Just 2 -- gives Just 3
Обратите внимание, что структура может повлиять на результат, например:
pure (+) <*> Nothing <*> Just 2 -- gives Nothing
Сравните это с обычной функцией zipWith
:
zipWith (+) [1] [2]
Вместо просто списков аппликативная работа работает для всех видов структур. Кроме того, умный трюк с pure
и (<*>)
обобщает zipping для работы с любым количеством аргументов. Чтобы увидеть, как это работает, проверьте следующие типы, сохранив при этом концепцию частично прикладных функций:
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
Как это связано с использованием Monads в качестве средства структурирования вычислений? Рассмотрим пример игрушек, в котором вы делаете два последовательных запроса из некоторой базы данных. Первый запрос возвращает вам некоторое ключевое значение, с которым вы хотите выполнить другой поиск. Проблема здесь в том, что первое значение обернуто внутри Maybe
, поэтому вы не можете напрямую запрашивать это. Вместо этого, возможно, это Functor
, вместо этого вместо fmap
возвращаемое значение с новым запросом. Это даст вам два вложенных значения Maybe
, как указано выше. Другой запрос приведет к трем слоям Maybe
s. Это было бы довольно сложно запрограммировать, но монадический join
дает вам способ сгладить эту структуру и работать только с одним уровнем Maybe
s.
(Я думаю, что я буду редактировать этот пост много, прежде чем это будет иметь смысл.)
Я думаю, что для понимания монад нужно играть с оператором связывания (>>=
). Сильное влияние [ http://dev.stephendiehl.com/hask/#eightfold-path-to-monad-satori]( Не читайте учебники по монаде.)
Моя маленькая игра заключается в следующем:
Адаптировано с 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 => ma → (a → mb) → mb
подпись.
Адаптация из 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"
- Nothing
... как описано в https://www.slideshare.net/ScottWlaschin/functional-design-patterns-devternity2018