问题描述
我正在尝试如 ExceptT(StateT AM),对于某些具体类型 A 和monad M ,并将它们包装到我的新自定义monads中。首先我确定出现 StateT AM 通常在其他上下文中,因此我决定最好将它单独包装在单元 M1 中,然后包装 ExceptT a M1 到 M2 。
所需的属性是使 M1 和 M2 MonadState 的实例和 M $ c $的类(让我们假设它被称为 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 , ExceptT , ContT $ c> $ 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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!