整理Monads - 将monad变换器的应用转变为新型monad [英] Tidying up Monads - turning application of a monad transformer into newtype monad

查看:132
本文介绍了整理Monads - 将monad变换器的应用转变为新型monad的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试如 ExceptT(StateT AM),对于某些具体类型 A 和monad M ,并将它们包装到我的新自定义monads中。

首先我确定出现 StateT AM 通常在其他上下文中,因此我决定最好将它单独包装在单元 M1 中,然后包装 ExceptT a M1 M2



所需的属性是使 M1 M2 MonadState 的实例和 M MyMonadClass )。 M2 应该是 MonadError 的一个实例。



首先我从简单类型同义词开始:

  type MyState = StateT AM 
类型MyBranch a =除了MyState

然后我想我会首先勾画实例声明(不实现实例)并且这是我第一次得到的卡住。 实例MonadState A(MyState)似乎不是正确的语法。我想我必须创建 newtype MyState'a = StateT a M ,然后 type MyState = MyState A (Lets不要在不必要的地方使用语言扩展)。

然而,一旦我开始将同义词转换为 newtype 声明,我开始失去与 StateT AM ExceptT ... 类型的连接。

  newtype MyState'sa = MyState'{runMyState :: s  - > (s,a)} 
type MyState = MyState'A
newtype MyBranch ea = MyBranch {runMyBranch :: MyState(ea)}

现在已经实施的变形金刚消失了,我想我正在尝试做一些没有多大意义的事情。所以我的问题是:如何正确地将这种行为包装到新的复合monad中,以便访问下面的图层,以避免不必要的提升,并保持事情清晰和组织良好。

解决方案

正常模式是为您的整个变压器堆栈定义一个新类型。

  data A = A 
data E = E

newtype MyBranchT ma = MyBranchT {getMyBranchT :: ExceptT E(StateT a m)a}

如果堆栈中有任何部分自行添加有意义的新功能,您还需要为这些部分定义新类型。

b
$ b

然后使用 GeneralizedNewtypeDeriving 来获取所有monad类的实例。

  { - #LANGUAGE GeneralizedNewtypeDeriving# - } 

- base
import Control.Applicative
import Control.Monad

- 变形金刚
导入Control.Monad.IO.Class
导入Control.Monad.Trans.Class
导入Control.Monad.Trans.Except
导入Control.Monad.Trans.State.Lazy

- mtl类
导入Control.Monad.Cont.Class
导入Control.Monad.Error.Class
导入Control.Monad.Reader.Class
导入Control.Monad.State.Class
import Control.Monad.Writer.Class

data A = A
data E = E

newtype MyBranchT ma = MyBranchT {getMyBranchT :: ExceptT E( StateT a m)a}
派生(Functor,Applicative,Monad,MonadIO, - 基类和变换器的类
{ - Alternative,MonadPlus, - } - 如果E是幺半群
MonadState A,MonadError E, - MyBranchT提供的来自mtl的类
MonadCont,MonadReader r,MonadWriter w) - 可能从m

您必须亲自编写 MonadTrans 实例。 lift 永远只是 lift 为堆栈中的每个转换器添加一次。

 实例MonadTrans MyBranchT其中
lift = MyBranchT。电梯 。提升

如果有任何堆栈增加了有意义的新功能 X 你自己也可以为这些功能定义一个新的 MonadX 类,编写 MonadX StateT , ExceptT ContT $ c>等),并为你的变量栈( MyBranchT )派生 MonadX 实例。 p>

您通常还会为 MyBranchT Identity runMyBranchT runMyBranch

  import Data.Functor.Identity 

类型MyBranch a = MyBranchT标识a

runMyBranchT :: MyBranchT ma - > A - > m(E a,A)
runMyBranchT mb s = runStateT(runExceptT(getMyBranchT mb))s

runMyBranch :: MyBranch a - > A - > (E a,A)
runMyBranch mb s = runIdentity $ runMyBranchT mb s


I am trying to take e.g. ExceptT a (StateT A M), for some concrete type A and monad M, and wrap them up into my new custom monads.

First I identified that StateT A M appears often in other contexts and thus I decided it would be best to wrap that alone in a monad M1 and then wrap ExceptT a M1 into M2.

The desired property is to make M1 and M2 instances of MonadState and the class of M (lets assume it is called MyMonadClass). Also M2 should be an instance of MonadError.

First I started by simple type synonyms:

type MyState    = StateT A M
type MyBranch a = ExceptT a MyState

then I thought that I would first sketch the instance declarations (without implementing the instance) and thats where I first got stuck. instance MonadState A (MyState) seemed to not be the right syntax. I thought I would have to create newtype MyState' a = StateT a M and then type MyState = MyState A (Lets not use language extensions where not necessary).

However once I started converting the synonyms into newtype declarations I started to lose the connection to the StateT A M and ExceptT ... types.

newtype MyState' s a = MyState' { runMyState :: s -> (s, a) }
type MyState = MyState' A
newtype MyBranch e a = MyBranch { runMyBranch :: MyState (Either e a) }

Now the transformers that are already implemented disappeared and I think I am trying to do something that does not make much sense. So my question is: How would one correctly wrap this kind of behaviour into new compound monads that make accessible the layers underneath so that one avoids unnecessary lifting and keeps things clear and well-organized.

解决方案

The normal pattern is to define a newtype for your complete transformer stack.

data A = A
data E = E

newtype MyBranchT m a = MyBranchT { getMyBranchT :: ExceptT E (StateT A m) a }

If there are any pieces of the stack that add meaningful new capabilities on their own you'd also define newtypes for those pieces.

Then you use GeneralizedNewtypeDeriving to get instances for all of the various monad classes.

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

-- base
import Control.Applicative
import Control.Monad

-- transformers
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Control.Monad.Trans.Except
import Control.Monad.Trans.State.Lazy

-- mtl classes
import Control.Monad.Cont.Class
import Control.Monad.Error.Class
import Control.Monad.Reader.Class
import Control.Monad.State.Class
import Control.Monad.Writer.Class

data A = A
data E = E

newtype MyBranchT m a = MyBranchT { getMyBranchT :: ExceptT E (StateT A m) a }
    deriving (Functor, Applicative, Monad, MonadIO,     -- classes from base and transformers
              {- Alternative, MonadPlus, -}             -- if E is a monoid
              MonadState A, MonadError E,               -- classes from mtl that MyBranchT provides
              MonadCont, MonadReader r, MonadWriter w)  -- classes from mtl that might be available from m

You'll have to write the MonadTrans instance by hand. lift is always just lift once for each of the transformers in the stack.

instance MonadTrans MyBranchT where
    lift = MyBranchT . lift . lift

If there are any pieces of the stack that add meaningful new capabilities X on their own you'd also define a new MonadX class for those capabilities, write MonadX instances for each of the monad transfomers (StateT, ExceptT, ContT, etc) if possible, and derive the MonadX instance for your transformer stack (MyBranchT).

You'll also usually make a type synonym for MyBranchT Identity and functions to runMyBranchT and runMyBranch

import Data.Functor.Identity

type MyBranch a = MyBranchT Identity a

runMyBranchT :: MyBranchT m a -> A -> m (Either E a, A)
runMyBranchT mb s = runStateT (runExceptT (getMyBranchT mb)) s

runMyBranch :: MyBranch a -> A -> (Either E a, A)
runMyBranch mb s = runIdentity $ runMyBranchT mb s

这篇关于整理Monads - 将monad变换器的应用转变为新型monad的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆