Haskell 具有多个相同变压器的monad堆栈

Haskell 具有多个相同变压器的monad堆栈,haskell,monads,monad-transformers,Haskell,Monads,Monad Transformers,我试图编写我自己的monad转换器,在堆栈上使用不同类型的多个相同monad转换器是有意义的。这个问题可以用阅读器monad来说明 reader monad是一种保存给定类型的只读上下文的方法 ex1 :: Reader Bool Bool ex1 = ask 或 monad转换器允许对下划线monad进行限制较少的假设 ex3 :: (MonadReader Bool m) => m Bool ex3 = ask 但是,如果我想要一个以上的只读环境,该怎么办?我可以写一个函数,比如

我试图编写我自己的monad转换器,在堆栈上使用不同类型的多个相同monad转换器是有意义的。这个问题可以用阅读器monad来说明

reader monad是一种保存给定类型的只读上下文的方法

ex1 :: Reader Bool Bool
ex1 = ask

monad转换器允许对下划线monad进行限制较少的假设

ex3 :: (MonadReader Bool m) => m Bool
ex3 = ask
但是,如果我想要一个以上的只读环境,该怎么办?我可以写一个函数,比如

ex4 :: (MonadReader Bool m, MonadReader Char m) => m Bool
ex4 = ask
然而,据我所知,没有办法运行ex4,因为

class Monad m => MonadReader r m | m -> r 

意味着每个单子都有一个唯一的读取类型。对于同一堆栈上的多个变压器是否有标准的解决方案?我应该完全避免这种情况吗?

一种相对简单的处理方法是创建一个表示要跟踪的状态的类型。假设您希望跟踪Bool和Char,如示例中所示

data MyState = MyState { getBool :: Bool, getChar :: Char }

f :: MonadReader MyState m => m Bool
f = asks getBool

其他人可能有更先进的解决方案

使用变压器和升降机到达您的内部单子:

import Control.Monad.Trans.Reader
import Control.Monad.Trans.Class (lift)

type MyMonad a = ReaderT Bool (Reader Char) a

askBool :: MyMonad Bool
askBool = ask
askChar :: MyMonad Char
askChar = lift ask

您提供的代码没有使用任何monad转换器(直接)。它使用了reader monad(恰好是应用于identity monad的转换器)和
monader
类型类。正如您所注意到的,MonadReader所暗示的类型函数不能为同一输入(monad
m
)产生两种不同的输出(环境类型)。

提升询问是否有效?@arrowd类型签名本身是非法的。MonadReader类型类有一个函数依赖项,表示monad
m
必须唯一地确定正在读取的类型。如果您使用显式monad transformer堆栈而不是类型类,那么它确实可以工作,但请记住
MonadReader
不是一个转换器。如果您使用transformer,则使用
transformers
包,避免使用
mtl
提供的术语,那么您就可以了。
import Control.Monad.Trans.Reader
import Control.Monad.Trans.Class (lift)

type MyMonad a = ReaderT Bool (Reader Char) a

askBool :: MyMonad Bool
askBool = ask
askChar :: MyMonad Char
askChar = lift ask