为什么monad变压器与堆叠monads不同?

在很多情况下,我不清楚将两个单片机与变压器相结合而不是使用两个单独的单片机可以获得什么。 显然,使用两个单独的monad是一件麻烦事,可能会涉及到符号内的符号表示法,但是有些情况下它不够表达?

一个案例似乎是ListT上的StateT:结合monad并没有给你正确的类型,如果你确实通过一堆monad获得了正确的类型,比如Bar(其中Bar a =(Reader r(List(Writer w(Identity a))),它没有做正确的事情。

但是,我希望更一般和技术上的理解,确切知道什么是monad变压器,当他们是和不必要时,以及为什么。

为了让这个问题更具针对性:

  • 什么是没有相应变压器的monad的实际例子(这将有助于说明变压器可以做什么,只是堆叠monads不能)。
  • StateT和ContT是唯一一个给出的类型与m的组成不同的类型的变换器,对于基础monad m(不管它们是由哪个顺序组成的)。
  • (我对特定的实现细节并不感兴趣,关于不同的库选择,而是关于通过堆叠一堆monadic类型的构造函数将单个变换器/态射体添加为替代组合效果的一般(也可能是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>>=类函数根据它们所属的单元做了非常不同的事情。

    让我们考虑一下(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)只是一个Writer行为,它返回一个Maybe。 没有什么特别的事情发生,如果这个Maybe值结果是Nothing。 这在add1_W函数中有例子,我们必须对x执行一个案例分析。

    偏好MaybeT方法的另一个原因是我们可以编写通用于任何monad堆栈的代码。 例如,功能:

    square x = do tell ("computing the square of " ++ show x)
                  return (x*x)
    

    可以在任何具有Writer字符串的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)Writer String (Maybe Int)代码时,你的代码显式地揭示了monad的结构,使它不那么通用。 add1_W这个定义:

    add1_W e = do x <- e 
                  return $ do 
                    y <- x 
                    return $ y + 1
    

    仅适用于双层monad堆栈,而像square这样的函数则适用于更一般的设置。


    什么是没有相应变压器的monad的实际例子(这将有助于说明变压器可以做什么,只是堆叠monads不能)。

    IOST是这里的典型例子。

    StateT和ContT是唯一一个给出的类型与m的组成不同的类型的变换器,对于基础monad m(不管它们是由哪个顺序组成的)。

    不, ListT ma不是(同构于) [ma]

    newtype ListT m a =
      ListT { unListT :: m (Maybe (a, ListT m a)) }
    
    链接地址: http://www.djcxy.com/p/77367.html

    上一篇: Why are monad transformers different to stacking monads?

    下一篇: When a generic type would not be a monad?