gpt4 book ai didi

haskell - 整理 Monads - 将 monad 转换器的应用程序转变为新型 monad

转载 作者:行者123 更新时间:2023-12-04 03:50:24 29 4
gpt4 key购买 nike

我试图采取例如ExceptT a (StateT A M) , 对于某些具体类型 A和单子(monad)M ,并将它们包装到我的新自定义单子(monad)中。

首先我确定StateT A M经常出现在其他上下文中,因此我决定最好将其单独包装在 monad M1然后包装 ExceptT a M1进入 M2 .

所需的属性是使 M1M2 MonadState 的实例和 M 的类(假设它被称为 MyMonadClass )。还有M2应该是 MonadError 的一个实例.

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

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

然后我想我会先画出实例声明(不实现实例),这就是我第一次陷入困境的地方。 instance MonadState A (MyState)似乎不是正确的语法。我以为我必须创建 newtype MyState' a = StateT a M然后 type MyState = MyState A (让我们不要在不必要的地方使用语言扩展)。

但是,一旦我开始将同义词转换为 newtype我开始失去与 StateT A M 的连接和 ExceptT ...类型。
newtype MyState' s a = MyState' { runMyState :: s -> (s, a) }
type MyState = MyState' A
newtype MyBranch e a = MyBranch { runMyBranch :: MyState (Either e a) }

现在已经实现的转换器消失了,我想我正在尝试做一些没有多大意义的事情。所以我的问题是:如何将这种行为正确地包装到新的复合 monad 中,以便可以访问下面的层,从而避免不必要的提升,并使事情保持清晰和井井有条。

最佳答案

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

data A = A
data E = E

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

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

然后你使用 GeneralizedNewtypeDeriving获取所有各种 monad 类的实例。
{-# 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

你必须写 MonadTrans手工实例。 lift总是只是 lift堆栈中的每个变压器一次。
instance MonadTrans MyBranchT where
lift = MyBranchT . lift . lift

如果堆栈中的任何部分添加了有意义的新功能 X您还可以自行定义一个新的 MonadX那些能力的类,写 MonadX如果可能,每个 monad 转换器( StateTExceptTContT 等)的实例,并导出 MonadX变压器堆栈的实例 ( MyBranchT)。

您通常还会为 MyBranchT Identity 创建一个类型同义词。和函数到 runMyBranchTrunMyBranch
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

关于haskell - 整理 Monads - 将 monad 转换器的应用程序转变为新型 monad,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32572152/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com