Ваш тип выглядит немного как Apfelmus's operational
monad, также известный как the Freer
monad:
data Program inst a where
Return :: a -> Program inst a
Bind :: inst a -> (a -> Program inst b) -> Program inst b
instance Monad (Program inst) where
return = Return
Return x >>= f = f x
Bind i next >>= f = Bind i (fmap (>>= f) next)
-- plus the usual Functor and Applicative boilerplate
Program :: (* -> *) -> * -> *
представляет собой последовательность inst
, которые используют свой параметр типа, чтобы указать «тип возврата» выполнения этой команды в интерпретаторе. Конструктор Bind
принимает инструкцию и продолжение, которое может быть запущено после получения результата от интерпретатора. Обратите внимание, как a
экзистенциально квантуется, что отражает тот факт, что типы всех промежуточных шагов в вычислении не относятся к общему типу.
Важное различие между Program
и вашим типом заключается в том, что тип ответа определяется инструкцией, а не фиксируется по всему вычислению. Это позволяет нам делать более мелкие гарантии относительно ответа, который каждый запрос ожидает спровоцировать.
Например, вот состояние монады написано как Program
:
data StateI s r where
Get :: StateI s s
Put :: s -> StateI s()
type State s = Program (StateI s)
get :: State s s
get = Bind Get Return
put :: s -> State s()
put s = Bind (Put s) Return
modify :: (s -> s) -> State s()
modify f = do
x <- get
put (f x)
runState :: State s a -> s -> (s, a)
runState (Return x) s = (s, x)
runState (Bind Get next) s = runState (next s) s
runState (Bind (Put s) next) _ = runState (next()) s
Совместно Йонеды лемма говорит нам, что Program
изоморфна Free
. Интуитивно, это бесплатная монада, основанная на ->
Functor
экземпляре.Для некоторых операций, таких как левоассоциативные связки, Program
может быть более эффективным, чем Free
, поскольку его >>=
основан на функциональном составе, а не, возможно, на дорогостоящем fmap
ping произвольным Functor
.
Это можно рассматривать как свободную монаду, если она переписана 'data S req rsp r = Done r | Следующий req (rsp -> S req rsp r) 'Тогда это фактически свободная монада на функторе' Compose ((,) req) ((->) resp) 'или' data Request req resp r = Request req (resp - > r'. Очень часто используется конструктор этого типа для написания функтора для свободной монады, как вы можете видеть из некоторых примеров из http://degoes.net/articles/modern-fp. Обратите внимание, что все четыре конструктора его функтора «HttpF», «GET», «POST» и т. д., являются фактически этой формой. – Michael
В 'pipe' тип' Server' (или эквивалентно 'Client') является версией (monad transformer) Затем мы можем написать «let mkRequest :: Monad m => req -> Client req resp m resp; mkRequest = request' (Здесь предполагается, что основная монада предполагается, как если бы вы применяли' FreeT', а не 'Free', к функтору запроса). – Michael