Одна вещь, которая делает это запутанным, - то, что "популярные" функции как bind
и <*>
ориентированы на практику. Но чтобы понять концепции, проще сначала взглянуть на другие функции. Также стоит отметить, что монады выделяются, потому что они немного преувеличены по сравнению с другими связанными понятиями. Поэтому я начну с функторов.
Функторы предлагают функцию (в нотации Haskell) fmap :: (Functor f) => (a -> b) -> f a -> f b
. Другими словами, у вас есть контекст, в f
который вы можете поднять функцию. Как вы можете себе представить, почти все является функтором. Списки, Возможно, Либо, функции, ввод / вывод, кортежи, парсеры ... Каждый представляет контекст, в котором может появляться значение. Таким образом, вы можете написать чрезвычайно универсальные функции, которые работают практически в любом контексте, используя fmap
или его встроенный вариант <$>
.
Что еще вы хотите сделать с контекстами? Возможно, вы захотите объединить два контекста. Таким образом , вы можете получить обобщение zip :: [a] -> [b] -> [(a,b)]
, например , как это: pair :: (Monoidal f) => f a -> f b -> f (a,b)
.
Но потому , что это еще более полезным на практике, Haskell библиотеки вместо этого предлагают Applicative
, который представляет собой комбинацию Functor
и Monoidal
, а также Unit
, что только добавляет , что вы можете поместить значения «внутри» свой контекст с unit
.
Вы можете написать чрезвычайно общие функции, просто указав эти три вещи в контексте, в котором вы работаете.
Monad
это просто еще одна вещь, которую вы можете заявить в дополнение к этому. Раньше я не упоминал о том, что у вас уже есть два способа объединить два контекста: вы можете не только создавать pair
их, но и составлять их, например, иметь список списков. В контексте ввода-вывода примером может служить действие ввода-вывода, которое может читать другие действия ввода-вывода из файла, поэтому у вас будет тип FilePath -> IO (IO a)
. Как мы можем избавиться от этого стека, чтобы получить исполняемую функцию IO a
? Вот где приходит Monad
s join
, это позволяет нам комбинировать два сложенных контекста одного типа. То же самое касается парсеров, может быть и т. Д. И bind
это просто более практичный способ использованияjoin
Таким образом, монадический контекст должен предлагать только четыре вещи, и его можно использовать практически со всеми механизмами, разработанными для ввода-вывода, для синтаксических анализаторов, для сбоев и т. Д.