Подтвердить что ты не робот

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

Я очень смущен этими тремя понятиями.

Есть ли простые примеры для иллюстрации различий между Категория, моноиды и монады?

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

4b9b3361

Ответ 1

Это, вероятно, не тот ответ, который вы ищете, но здесь вы все равно:

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

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

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

A Категория

Категория состоит из набора (или класса) объектов и пучка стрелок, каждая из которых соединяет два объекта. Кроме того, для каждого объекта должна быть стрелка идентификации, связывающая этот объект с самим собой. Кроме того, если есть одна стрелка (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. Функторы отображают по общим структурам по одному в то время, но с аппликативным функтором вы можете объединить две или несколько структур. Для простейшего примера вы можете использовать аппликаторы для 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.

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

Ответ 2

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

Моя маленькая игра заключается в следующем:

1. Concat 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 => ma → (a → mb) → mb подпись.

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" - Nothing

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

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

enter image description here