Haskell-关于“ tell”工作原理的说明

时间:2018-12-19 03:37:35

标签: haskell

我遇到了一些代码,将递归调用记录在Fibonacci函数中。我已经对其进行了修改,以使其被记住。无论如何,我都在努力了解tell函数的工作方式……以及我如何才能跟踪此函数中的调用

import Debug.Trace
import Control.Monad.Trans.Writer
import Data.Monoid
imoort qualified Data.Vector as V

fib :: Int -> Writer (Sum Integer) Integer
fib x = f x
  where
    fibs = f <$> (V.fromList [0..x])
    f 0 = return 0
    f 1 = return 1
    f n =  do a <- fibs V.! (n-1)
              b <- fibs V.! (n-2)
              tell (Sum 2)
              return (a + b)

了解tell的工作原理真的很有帮助

1 个答案:

答案 0 :(得分:4)

  

逐步讲解如何行之有效

我尝试使用以下两行代码来说明tell函数的工作原理:

...
tell (Sum 2)
return (a + b)
..

source中学习它会很有帮助:

tell :: (Monad m) => w -> WriterT w m ()
tell w = writer ((), w)

writer :: (Monad m) => (a, w) -> WriterT w m a
writer = WriterT . return

如此

tell (Sum 2) 
= writer ((), Sum 2) 
= WriterT . return ((), Sum 2)
= WriterT (Identity ((), Sum 2)) 

创建类型为WriterT (Identity ((), Sum 2))的值WriterT (Sum a) Identity ()

由于WriterTMonad的实例,因此该答案开头的表达式可以转换为:

tell (Sum 2) >>= \_ -> return (a + b)

现在,我们需要查看source>>=的{​​{1}}的工作方式,如下所示:

Writer Monad

要逐步评估表达式m >>= k = WriterT $ do ~(x, w) <- runWriterT m ~(y, w') <- runWriterT (k x) return (y, w `mappend` w') ,如下所示:

tell (Sum 2) >>= \_ -> return (a + b)

~(x, w)  <- runWriterT m
= ~(x, w)  <- runWriterT (tell (Sum 2))
= ~(x, w)  <- runWriterT (WriterT (Identity ((), Sum 2)))
= ((), Sum 2)

~(y, w') <- runWriterT (k a)
= ~(y, w') <- runWriterT ((\_ -> return (a + b)) ())
= ~(y, w') <- runWriterT (return (a + b))
= ~(y, w') <- runWriterT ( WriterT (Identity ((a + b), mempty))
= ((a + b), mempty)

请注意,return (y, w `mappend` w') = return ((a + b), (Sum 2 `mappend` mempty)) = Identity ((a + b), Sum 2) return的内部Monad重载,而不是WriterT本身。

最后:

Writer Monad

tell (Sum 2) >>= \_ -> return (a + b) = WriterT (Identity ((a + b), Sum 2))

因此,f n = WriterT (Identity ((a + b), Sum 2)) 的功能只是将tell插入“ Sum 2”。