这种请求 - 响应类型是否有标准抽象?

Phi*_*sky 3 monads continuations haskell arrows haskell-pipes

我有以下类型:

data S req rsp = Done rsp | Next req (rsp -> S req rsp)
Run Code Online (Sandbox Code Playgroud)

我们的想法是将其用作网络通信的纯粹表示,即:

... Next GetUser $ \uid -> Next (Login uid) $ \success -> Done success
Run Code Online (Sandbox Code Playgroud)

然后由一些不纯的函数评估eval.

现在,这是什么(如果有的话?)就我所见,这不是一个单子,也不是一个箭头.它似乎是stream/pipe/automaton/fsm和continuation monad之间的东西.这让我觉得这种类型的东西可能有更好的代表性,但是什么呢?

Nik*_*kov 6

这是免费的Monad.这个想法是你有一个说明的描述,你可以有多个解释器,如你的eval功能.免费Monad抽象了这个任务的模式.有关详细信息,我建议这个伟大的职位.

要使您的类型适应,Free我们可以执行以下操作:

{-# LANGUAGE DeriveFunctor #-}

import Control.Monad.Free

data Instruction req rsp next =
  Respond rsp |
  Interact req (rsp -> next)
  deriving (Functor)

type S req rsp =
  Free (Instruction req rsp)

respond :: rsp -> S req rsp ()
respond rsp =
  liftF (Respond rsp)

interact :: req -> S req rsp rsp
interact req =
  liftF (Interact req id)
Run Code Online (Sandbox Code Playgroud)

现在,由于Free,S req rsp是一个单子,这意味着你现在可以撰写respondinteract使用功能的MonadAPI.

还有更多.可以使用Template Haskell生成respondinteract函数,并使用以下额外代码:

{-# LANGUAGE TemplateHaskell #-}

import Control.Monad.Free.TH

makeFree ''Instruction
Run Code Online (Sandbox Code Playgroud)


Ben*_*son 5

你的类型貌似有点Apfelmus的operational单子,也被称为Freer单子:

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
Run Code Online (Sandbox Code Playgroud)

Program :: (* -> *) -> * -> *表示一系列指令 inst,它们使用它们的类型参数来指示在解释器中运行该指令的"返回类型".所述Bind构造函数采用的指令,并且可在指令的结果已被从解析器接收后运行的继续.注意如何a进行存在量化,反映出计算中所有中间步骤的类型与整体类型无关的事实.

Program和你的类型之间的重要区别是响应的类型由指令决定,而不是固定在整个计算上.这使我们能够对每个请求期望引起的响应做出更细粒度的保证.

例如,这是状态monad写为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
Run Code Online (Sandbox Code Playgroud)

共同Yoneda引理告诉我们这Program是同构的Free.直观地说,它是一个基于自由单子->Functor实例.对于某些操作,如左关联绑定,Program可以更有效Free,因为它>>=基于函数组合,而不是可能昂贵地fmapping任意Functor.