【问题标题】:Avoiding lift with monad transformers使用单子变压器避免提升
【发布时间】:2012-02-21 16:58:56
【问题描述】:

我有一个问题,一堆单子变压器(甚至一个单子变压器)超过IO。一切都很好,除了在每个动作之前使用提升非常烦人!我怀疑这真的没什么可做的,但我想我还是会问的。

我知道提升整个块,但如果代码真的是混合类型怎么办?如果 GHC 加入一些语法糖会不会很好(例如,<-$ = <- lift)?

【问题讨论】:

    标签: haskell monads monad-transformers


    【解决方案1】:

    对于所有标准的mtl monad,您根本不需要liftgetputasktell — 它们都可以在任何单子中工作,并且在堆栈中的某个位置使用正确的变压器。缺少的部分是 IO,即使在那里,liftIO 也会将任意 IO 操作提升到任意数量的层。

    这是通过提供的每个“效果”的类型类完成的:例如,MonadState 提供 getput。如果您想围绕转换器堆栈创建自己的newtype 包装器,您可以使用GeneralizedNewtypeDeriving 扩展来执行deriving (..., MonadState MyState, ...),或者滚动您自己的实例:

    instance MonadState MyState MyMonad where
      get = MyMonad get
      put s = MyMonad (put s)
    

    您可以使用它来选择性地公开或隐藏组合转换器的组件,方法是定义一些实例而不是其他实例。

    (通过定义自己的类型类并为标准转换器提供样板实例,您可以轻松地将这种方法扩展到您自己定义的全新 monadic 效果,但全新的 monad 很少见;大多数时候,您将只需编写 mtl 提供的标准集即可。)

    【讨论】:

    • 哦,我觉得我很愚蠢,你提到在你之前的一个答案中,我当时无法理解。现在,我非常感谢!
    【解决方案2】:

    您可以通过使用类型类而不是具体的 monad 堆栈来使您的函数与 monad 无关。

    假设你有这个功能,例如:

    bangMe :: State String ()
    bangMe = do
      str <- get
      put $ str ++ "!"
      -- or just modify (++"!")
    

    当然,你意识到它也可以作为转换器,所以可以这样写:

    bangMe :: Monad m => StateT String m ()
    

    但是,如果您有一个使用不同堆栈的函数,比如说ReaderT [String] (StateT String IO) () 或其他什么,您将不得不使用可怕的lift 函数!那么如何避免呢?

    诀窍是使函数签名更加通用,以便它表示State monad 可以出现在 monad 堆栈中的任何位置。这样做是这样的:

    bangMe :: MonadState String m => m ()
    

    这会强制m 成为支持 monad 堆栈中任何位置(实际上)状态的 monad,因此该函数将在不提升任何此类堆栈的情况下工作。

    不过,有一个问题;因为IO 不是mtl 的一部分,所以默认情况下它没有转换器(例如IOT),也没有方便的类型类。那么当你想任意解除 IO 动作时应该怎么做呢?

    救援来了MonadIO!它的行为与MonadStateMonadReader 等几乎相同,唯一的区别是它的提升机制略有不同。它的工作原理如下:您可以采取任何IO 操作,并使用liftIO 将其转换为与monad 无关的版本。所以:

    action :: IO ()
    liftIO action :: MonadIO m => m ()
    

    通过以这种方式转换您希望使用的所有 monadic 动作,您可以随意地将 monad 交织在一起,而无需任何繁琐的提升。

    【讨论】:

    • 感谢您的详细解答!虽然在时间上被 ehird 打败了 ;)
    • 我和 ehird 为这个问题提供了一些不同的解决方案。可能值得阅读这两个回复以了解您拥有的替代方案:)
    • 很遗憾需要这么多样板文件。
    • 不管怎样,这个解决方案似乎需要{-# LANGUAGE FlexibleContexts #-} pragma。
    猜你喜欢
    • 1970-01-01
    • 2018-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-01
    • 2017-08-21
    相关资源
    最近更新 更多