我遇到了一些代码,将递归调用记录在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
的工作原理真的很有帮助
答案 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 ()
。
由于WriterT
是Monad
的实例,因此该答案开头的表达式可以转换为:
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
”。