gpt4 book ai didi

haskell - 为什么单子(monad)转换器与堆叠单子(monad)不同?

转载 作者:行者123 更新时间:2023-12-03 11:54:19 27 4
gpt4 key购买 nike

在许多情况下,我不清楚通过将两个 monad 与转换器组合而不是使用两个单独的 monad 可以获得什么。显然,使用两个单独的 monad 很麻烦,并且可能在 do 表示法中涉及 do 表示法,但是否存在表达力不够的情况?

一种情况似乎是 List 上的 StateT:组合 monads 不会让你得到正确的类型,如果你确实通过一堆像 Bar 这样的 monads 获得了正确的类型(其中 Bar a = (Reader r (List (Writer w (Identity a))),它没有做正确的事情。

但我想更全面和技术地了解 monad 转换器究竟带来了什么,它们什么时候是必要的,什么时候不是必要的,以及为什么。

为了使这个问题更加集中:

  • 什么是没有相应转换器的 monad 的实际示例(这将有助于说明转换器可以做什么,而只是堆叠 monad 不能)。
  • StateT 和 ContT 是唯一的转换器,它们给出的类型不等于它们与 m 的组合,对于底层的单子(monad) m (不管它们是按什么顺序组成的。)

  • (我对关于库的不同选择的特定实现细节不感兴趣,而是关于通过堆叠一堆单子(monad)类型构造函数来添加什么单子(monad)更改器(mutator)/态射作为组合效果的替代方案的一般(并且可能与 Haskell 无关)问题.)

    (为了提供一点上下文,我是一名语言学家,他正在做一个项目来丰富蒙太古语法 - 只需输入 lambda 演算,即可将单词含义组合成句子 - 使用 monad 转换器堆栈。了解转换器是否真的在做真的很有帮助任何对我有用的东西。)

    谢谢,

    鲁本

    最佳答案

    回答您关于 Writer w (Maybe a) 之间区别的问题对比 MaybeT (Writer w) a ,让我们先来看看定义:

    newtype WriterT w m a = WriterT { runWriterT :: m (a, w) }
    type Writer w = WriterT w Identity

    newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

    使用 ~~要表示“结构上类似于”,我们有:
    Writer w (Maybe a)  == WriterT w Identity (Maybe a)
    ~~ Identity (Maybe a, w)
    ~~ (Maybe a, w)

    MaybeT (Writer w) a ~~ (Writer w) (Maybe a)
    == Writer w (Maybe a)
    ... same derivation as above ...
    ~~ (Maybe a, w)

    所以在某种意义上你是正确的——结构上都是 Writer w (Maybe a)MaybeT (Writer w) a是相同的 - 两者本质上只是一对 Maybe 值和一个 w .

    不同之处在于我们如何将它们视为一元值。 return>>=类函数根据不同的情况做非常不同的事情
    它们属于哪个 monad。

    让我们考虑这对 (Just 3, []::[String]) .使用关联
    我们在上面得出了这对如何在两个 monad 中表示:
    three_W :: Writer String (Maybe Int)
    three_W = return (Just 3)

    three_M :: MaybeT (Writer String) Int
    three_M = return 3

    下面是我们如何构造一对 (Nothing, []) :
    nutin_W :: Writer String (Maybe Int)
    nutin_W = return Nothing

    nutin_M :: MaybeT (Writer String) Int
    nutin_M = MaybeT (return Nothing) -- could also use mzero

    现在考虑对这个函数:
    add1 :: (Maybe Int, String) -> (Maybe Int, String)
    add1 (Nothing, w) = (Nothing w)
    add1 (Just x, w) = (Just (x+1), w)

    让我们看看如何在两个不同的 monad 中实现它:
    add1_W :: Writer String (Maybe Int) -> Writer String (Maybe Int)
    add1_W e = do x <- e
    case x of
    Nothing -> return Nothing
    Just y -> return (Just (y+1))

    add1_M :: MaybeT (Writer String) Int -> MaybeT (Writer String) Int
    add1_M e = do x <- e; return (e+1)
    -- also could use: fmap (+1) e

    一般来说,您会看到 MaybeT monad 中的代码更加简洁。

    此外,从语义上讲,这两个单子(monad)非常不同......
    MaybeT (Writer w) a是一个可能失败的 Writer-action,失败是
    自动为您处理。 Writer w (Maybe a)只是一个作家
    返回一个 Maybe 的 Action 。如果那个 Maybe 值没有什么特别的事情发生
    原来是Nothing。这在 add1_W 中有示例。函数在哪里
    我们必须对 x 进行案例分析.

    更喜欢 MaybeT 的另一个原因方法是我们可以编写代码
    这在任何 monad 堆栈上都是通用的。例如,函数:
    square x = do tell ("computing the square of " ++ show x)
    return (x*x)

    可以在任何具有 Writer String 的 monad 堆栈中原样使用,例如:
    WriterT String IO
    ReaderT (WriterT String Maybe)
    MaybeT (Writer String)
    StateT (WriterT String (ReaderT Char IO))
    ...

    但是 square 的返回值不对 Writer String (Maybe Int) 进行类型检查因为 square不返回 Maybe .

    当您在 Writer String (Maybe Int) 中编码时,您的代码明确显示
    monad 的结构使其不那么通用。 add1_W 的这个定义:
    add1_W e = do x <- e 
    return $ do
    y <- x
    return $ y + 1

    仅在两层 monad 堆栈中有效,而像 square 这样的函数
    在更一般的环境中工作。

    关于haskell - 为什么单子(monad)转换器与堆叠单子(monad)不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38710154/

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