Ранее Я спросил о переводе монадического кода на использование только экземпляра прикладного функционала Parsec. К сожалению, у меня появилось несколько ответов, которые отвечали на вопрос, который я буквально задавал, но на самом деле не очень хорошо понимали. Поэтому позвольте мне попробовать это снова...
Подводя итог моим знаниям, прикладной функтор - это нечто более ограниченное, чем монада. В традиции "меньше - больше", ограничение того, что может сделать код, увеличивает возможности для сумасшедшего манипулирования кодом. Несмотря на это, многие люди, похоже, считают, что использование аппликативного вместо монады - превосходное решение, где это возможно.
Класс Applicative
определяется в Control.Applicative
, чей список Haddock помогает с помощью методов класса и функций полезности разделять классные экземпляры классов между ними, чтобы затруднить быстрый просмотр всего экрана сразу. Но соответствующие сигнатуры типа
pure :: x -> f x
<*> :: f (x -> y) -> f x -> f y
*> :: f x -> f y -> f y
<* :: f x -> f y -> f x
<$> :: (x -> y) -> f x -> f y
<$ :: x -> f y -> f x
Отличный смысл, правильно?
Ну, Functor
уже дает нам fmap
, что в основном <$>
. I.e., Учитывая функцию от x
до y
, мы можем сопоставить f x
с a f y
. Applicative
добавляет два существенно новых элемента. Один из них pure
, который имеет примерно тот же тип, что и return
(и несколько других операторов в классах теории категорий). Другой - <*>
, что дает нам возможность взять контейнер функций и контейнер входов и создать контейнер с выходами.
Используя вышеприведенные операторы, мы можем очень аккуратно сделать что-то вроде
foo <$> abc <*> def <*> ghi
Это позволяет нам взять N-арную функцию и передать ее аргументы из N функторов таким образом, который легко обобщается на любой N.
Это я уже понимаю. Есть две основные вещи, которые я еще не понимаю.
Во-первых, функции *>
, <*
и <$
. Из их типов <* = const
, *> = flip const
и <$
может быть что-то подобное. Предположительно, это не описывает, что на самом деле выполняют эти функции. (??!)
Во-вторых, при написании парсера Parsec каждый анализируемый объект обычно выглядит примерно так:
entity = do
var1 <- parser1
var2 <- parser2
var3 <- parser3
...
return $ foo var1 var2 var3...
Поскольку аппликативный функтор не позволяет связать промежуточные результаты с переменными таким образом, я озадачен тем, как их собрать на заключительном этапе. Я не смог полностью обдумать идею, чтобы понять, как это сделать.