我对liftM
如何保存上下文感到困惑,特别是在Writer
单子的情况下。我一直在经历“学到一个伟大的Haskell为您服务”,并且一直停留在liftM
的解释上。
这是一个例子:
ghci> runWriter $ liftM not $ Writer (True, "chickpeas")
(False,"chickpeas")
我了解
liftM
将not
函数提升到monad并将其应用于内部值(True
)的概念,或者不对等号(“鹰嘴豆”)进行任何处理,或者将其与标识monoid结合使用字符串(“”)。但是,
liftM
的实现如下:liftM :: (Monad m) => (a -> b) -> m a -> m b
liftM f m = m >>= (\x -> return (f x))
在输入到函数中的monad内部的
f
值上应用x
是有意义的。但是,如果执行return (f x)
,为什么我不找回在默认monad上下文中包装的所有内容?在上面的f x
示例中,我希望Writer
生成runWriter $ return (f x)
,因为默认的(False, "")
实例的“ monoid”值为“”。我想念什么?
最佳答案
您已经非常专注于\x -> return (f x)
,以至于完全忘记了它之前的m >>=
!
关于return
的功能,您绝对正确:
Control.Monad.Writer> return (not True) :: Writer String Bool
WriterT (Identity (False,""))
您忘了的一点是绑定,它是这样实现的(直到某些
newtype
和转换器废话):m >>= f = (val', monoid <> monoid') where
(val, monoid) = m
(val', monoid') = f val
在我们的例子中,该
monoid'
部分将是""
,但是monoid
将是"chickpeas"
,因此不会丢失。详细地:(True, "chickpeas") >>= (\x -> return (not x))
= { definition of bind }
(val', monoid <> monoid') where
(val, monoid) = (True, "chickpeas")
(val', monoid') = (\x -> return (not x)) val
= { substitute away val and monoid everywhere }
(val', "chickpeas" <> monoid') where
(val', monoid') = (\x -> return (not x)) True
= { evaluate the lambda and not }
(val', "chickpeas" <> monoid') where
(val', monoid') = return False
= { definition of return }
(val', "chickpeas" <> monoid') where
(val', monoid') = (False, "")
= { substitute away val' and monoid' everywhere }
(False, "chickpeas" <> "")
= { evaluate <> }
(False, "chickpeas")