Reu*_*ben 21 monads haskell monad-transformers category-theory
在许多情况下,我不清楚将两个monad与变压器组合而不是使用两个单独的monad可以获得什么.显然,使用两个独立的monad是一件麻烦事,并且可能涉及到符号内部的符号,但是有些情况下它只是表达不够吗?
一个案例似乎是列表上的StateT:组合monads不能得到正确的类型,如果你通过像Bar这样的monad栈获得正确的类型(其中Bar a =(Reader r(List(Writer w(Identity) a))),它没有做正确的事情.
但是我想更准确地理解monad变压器带来什么,当它们是否必要时,以及为什么.
为了使这个问题更加集中:
(关于库的不同选择,我对特定的实现细节不感兴趣,而是对monad变换器/态射正在添加的一般问题(可能是Haskell独立)的问题,作为通过堆叠一堆monadic类型构造函数来组合效果的替代方法.)
(为了给出一点背景,我是一个语言学家,正在做一个丰富蒙塔古语法的项目 - 简单地输入lambda演算,用于将单词意义组成句子 - 用monad变换器堆栈.理解变换器是否真的在做真的很有帮助对我有用的任何东西.)
谢谢,
鲁本
Eri*_*ikR 22
要回答关于Writer w (Maybe a)vs 之间区别的问题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) }
Run Code Online (Sandbox Code Playgroud)
使用~~是指"结构上类似于"我们有:
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)
Run Code Online (Sandbox Code Playgroud)
因此,在某种意义上,你是正确的-这两个结构Writer w (Maybe a),并MaybeT (Writer w) a
是相同的-都是基本上只是一对也许值和的w.
不同之处在于我们如何将它们视为monadic值.在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
Run Code Online (Sandbox Code Playgroud)
以下是我们如何构建一对(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
Run Code Online (Sandbox Code Playgroud)
现在考虑对这个函数:
add1 :: (Maybe Int, String) -> (Maybe Int, String)
add1 (Nothing, w) = (Nothing w)
add1 (Just x, w) = (Just (x+1), w)
Run Code Online (Sandbox Code Playgroud)
让我们看看我们将如何在两个不同的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
Run Code Online (Sandbox Code Playgroud)
通常,您会看到MaybeT monad中的代码更简洁.
而且,在语义上两个单子是非常不同的......
MaybeT (Writer w) a是一个可能失败的Writer动作,并且会自动为您处理失败.Writer w (Maybe a)只是一个返回Maybe的Writer动作.如果那个Maybe值变成Nothing,那么什么都不会发生.这add1_W在我们必须执行案例分析的功能中举例说明x.
更喜欢这种MaybeT方法的另一个原因是我们可以编写在任何monad堆栈上都是通用的代码.例如,功能:
square x = do tell ("computing the square of " ++ show x)
return (x*x)
Run Code Online (Sandbox Code Playgroud)
可以在任何具有Writer字符串的monad堆栈中保持不变,例如:
WriterT String IO
ReaderT (WriterT String Maybe)
MaybeT (Writer String)
StateT (WriterT String (ReaderT Char IO))
...
Run Code Online (Sandbox Code Playgroud)
但返回值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
Run Code Online (Sandbox Code Playgroud)
仅适用于双层monad堆栈,而函数类似于square
更常规的设置.
什么是没有相应变压器的monad的实际示例(这将有助于说明变压器可以做什么只是堆叠monad不能).
IO这ST是规范的例子.
StateT和ContT是唯一的变换器,它们给出的类型与m的组成不等同于m,对于底层monad m(无论它们是由哪个顺序组成的.)
不,ListT m a不是(同构)[m a]:
newtype ListT m a =
ListT { unListT :: m (Maybe (a, ListT m a)) }
Run Code Online (Sandbox Code Playgroud)