为什么liftM不破坏环境?

时间:2017-10-10 15:53:57

标签: haskell functional-programming

我对liftM如何保留上下文感到困惑,特别是在Writer monad的情况下。我一直在经历“让你学习哈斯克尔以获得伟大的利益”,并坚持对liftM的解释。

以下是一个例子:

ghci> runWriter $ liftM not $ Writer (True, "chickpeas")
(False,"chickpeas")

我理解liftMnot函数提升到monad中的概念,将其应用于内部的值(True),并且对monoid(“鹰嘴豆”)不执行任何操作)或将其与字符串(“”)的身份monoid组合。

但是,liftM的实施方式如下:

liftM :: (Monad m) => (a -> b) -> m a -> m b  
liftM f m = m >>= (\x -> return (f x))

f应用于正在输入函数的monad中的x值是有意义的。但是,如果我执行return (f x),为什么我不能找回在默认 monad上下文中包含的f x生成内容?在上面的Writer示例中,我希望runWriter $ return (f x)生成(False, ""),因为默认的Writer string bool实例具有“”作为其monoid值。

我错过了什么?

2 个答案:

答案 0 :(得分:5)

你已经非常专注于\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")

答案 1 :(得分:3)

关键是>>= Writer的定义。以下内容已经过简化,因为Writer是根据WriterT Identity

定义的
w >>= f = Writer (m <> n, b)
  where (m, a) = runWriter w
        (n, b) = runWriter (f a)

现在,如果我们使用liftM的此定义展开>>=,您可以看到如何累积monoidal writer-value。

liftM f w = w >>= \x -> return (f x)

          {- expand definiton of (>>=) -}
          = Writer (m <> n, b)
                where (m, a) = runWriter w
                      (n, b) = runWriter ((\x -> return (f x)) a)

          {- apply (\x -> return (f x)) to its argument -}
          = Writer (m <> n, b)
                where (m, a) = runWriter w
                      (n, b) = runWriter (return (f a))

          {- expand definition of (return (f a)) -}
          = Writer (m <> n, b)
                where (m, a) = runWriter w
                      (n, b) = runWriter (Writer ("", f a))

          {- runWriter . Writer === id -}
          = Writer (m <> n, b)
                where (m, a) = runWriter w
                      (n, b) = ("", f a)

          {- inline 'n' and 'b' -}
          = Writer (m <> "", f a) where (m, a) = runWriter w

          {- "" is the identity element -}
          = Writer (m, f a) where (m, a) = runWriter w

从最后的等式中,您可以看到liftM f (Writer (m, a))f应用于a内的Writer值,同时保留之前的幺半群值{{1} }。