Haskell 为什么单子变压器不同于堆叠单子?
在许多情况下,我不清楚通过将两个单子与一个转换器组合而不是使用两个单独的单子可以得到什么。显然,使用两个单独的单子是一件麻烦的事,可能会在do符号中包含do符号,但在某些情况下,它的表达能力不够吗 一种情况似乎是列表上的StateT:组合monad并不能得到正确的类型,如果您确实通过一堆monad(如Bar)获得正确的类型(其中Bar a=(Reader r(List)(Writer w(Identity a))),它就做不到正确的事情 但我想更全面、更技术地了解monad Transformer到底带来了什么,何时需要,何时不需要,以及为什么 要使这个问题更加集中,请执行以下操作:Haskell 为什么单子变压器不同于堆叠单子?,haskell,monads,monad-transformers,category-theory,Haskell,Monads,Monad Transformers,Category Theory,在许多情况下,我不清楚通过将两个单子与一个转换器组合而不是使用两个单独的单子可以得到什么。显然,使用两个单独的单子是一件麻烦的事,可能会在do符号中包含do符号,但在某些情况下,它的表达能力不够吗 一种情况似乎是列表上的StateT:组合monad并不能得到正确的类型,如果您确实通过一堆monad(如Bar)获得正确的类型(其中Bar a=(Reader r(List)(Writer w(Identity a))),它就做不到正确的事情 但我想更全面、更技术地了解monad Transforme
IO
和ST
是这里的典型例子
StateT和ContT是唯一一种类型不等同于它们与m的组合的转换器,对于底层的monad m(无论它们的组合顺序如何)
不,不是(同构于)[MA]
:
newtype ListT m a =
ListT { unListT :: m (Maybe (a, ListT m a)) }
要回答您关于
Writer w(maybea 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(可能是a)
和MaybeT(Writer w)a
都是一样的-两者本质上只是一对a值和aw
区别在于我们如何将它们视为一元值。
返回
和>=
类函数根据不同的情况执行不同的操作
他们属于哪个单子
让我们考虑这两个代码<代码>(仅3,[]::[String ])< /代码>。 我们在上面推导了这一对在两个单子中的表达方式:
three_W :: Writer String (Maybe Int)
three_W = return (Just 3)
three_M :: MaybeT (Writer String) Int
three_M = return 3
下面是我们如何构造一对(Nothing,[])
:
现在考虑这对函数:
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
可以在任何具有写入器字符串的monad堆栈中不加更改地使用,例如:
WriterT String IO
ReaderT (WriterT String Maybe)
MaybeT (Writer String)
StateT (WriterT String (ReaderT Char IO))
...
但是square
的返回值没有针对编写器字符串(可能是Int)
进行类型检查,因为square
没有返回可能是
当您在编写器字符串(可能是Int)
中编码时,代码会显式地显示
单子的结构使它不那么通用。add1\u W
:
add1_W e = do x <- e
return $ do
y <- x
return $ y + 1
add1_W e=do x“使用两个单独的monad”是什么意思?你能举个例子吗?列表monad有点特殊,因为任何作为列表的表达式也是列表monad中的一个值。尝试“堆栈”Writer和IO-Writer W(IO a)
是Writer
单子中返回IO a
的一个值,这与Writer w IO a
不同,后者是Writer w IO
单子中返回a
的一个值。嗯,IO也可能不是最好的例子。那么(Writer w(可能a))与(MaybeT(Writer w)a)呢?我认为,这些都是相同的类型,经过必要的修改后,它们的作用也一样。一个单子变换器只是一个类型t
,其中tm
是一个monad
,对于任何monad
m。然而,一般来说,两个单子的组合不是一个单子-因此,没有太多的抽象操作(如果有的话)可以用两个单子的组合来定义(如果没有抽象,在Haskell中用单子编程将是非常乏味的)。这只是巧合,大多数转换器通常会遇到单子的组合(即WriterT x m
是Compose m(x,)
)@user2407038我不确定大多数变形金刚最终看起来都像合成变形金刚是不是巧合。制作变形金刚最简单的方法是粗略地看一看,对于某个特定的单子,它与任何其他单子的合成都是单子。我们可以用特定的单子(实现特定于该单子)来实现这一点但并不是所有的都是变压器的基本来源。你说得对,这不是必要的,但这不是巧合。可能有意思的是,它仍然几乎是合成,你只需要将合成移动到固定点下:List a=Fix(1+a*x)
和ListT m a=Fix(m∘ (1+a*x))
我认为没有人知道没有转换器的monad的显式示例。IO和ST monad是不透明的-它们不是作为显式类型表达式给出的,它们是由库以某种方式在低级别实现的。所有已知的显式monad都有transformer
square x = do tell ("computing the square of " ++ show x)
return (x*x)
WriterT String IO
ReaderT (WriterT String Maybe)
MaybeT (Writer String)
StateT (WriterT String (ReaderT Char IO))
...
add1_W e = do x <- e
return $ do
y <- x
return $ y + 1