【问题标题】:monad stack with multiple of the same transformer具有多个相同变压器的 monad 堆栈
【发布时间】:2019-01-11 19:42:35
【问题描述】:

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

reader monad 被提供为一种保存给定类型的只读上下文的方式

ex1 :: Reader Bool Bool
ex1 = ask

ex2 :: Reader Char Bool
ex2 = pure True

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 

表示每个 MonadReader 都有唯一的阅读类型。是否有针对同一堆栈上的多个变压器的标准解决方法?我应该尽量避免这种情况吗?

【问题讨论】:

  • lift ask 不工作吗?
  • @arrowd 类型签名本身就是非法的。 MonadReader 类型类有一个函数依赖,它表示 monad m 必须唯一地确定正在读取的类型。如果您使用显式的 monad 转换器堆栈而不是类型类,它确实有效
  • 请记住MonadReader 不是变压器。如果您使用变压器,例如transformers 包,并且避免考虑mtl 提供的条款,那么您会做得很好。

标签: haskell monads monad-transformers


【解决方案1】:

使用变压器和电梯进入你的内心单子:

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

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

【讨论】:

    【解决方案2】:

    以相对简单的方式处理它的一种方法是创建一个表示您想要跟踪的状态的类型。假设您要跟踪示例中的 Bool 和 Char

    data MyState = MyState { getBool :: Bool, getChar :: Char }
    
    f :: MonadReader MyState m => m Bool
    f = asks getBool
    

    其他人可能有更高级的解决方案!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-03
      相关资源
      最近更新 更多