Haskell的WriterMonad解构

来源:互联网 发布:菊正宗化妆水 知乎 编辑:程序博客网 时间:2024/06/07 06:20

问题导出

move i = do    x <- left i    tell "Go"    y <- left x    return y

其中

left i = writer ( x -1,"move left\t'):t tell "G0" :: MonadWriter [Char] m => m ()

问题

tell "Go"的上下文到底在哪里

我们一步步的解开

move  :: Int -> Writer String Intmove i = do    x <- left' i    _ <- tell "GO\t"    return x

我们把do展开

move i = let            ok p = do                    tell "go"                    return p            ok _ = fail ".."           in writer ((i-1),"Move Left\t") >>= ok

展开tell

move' i = let            ok p = do                    writer ((),"Go\t")                    return p            ok _ = fail ".."           in writer ((i-1),"Move Left\t") >>= ok

let解构转换为where解构,暂时消除掉i

move i = writer ((i-1),"Move Left\t") >>= ok where            ok p = do                    writer ((),"Go\t")                    return p

消除掉第二个do

move i = writer ((i-1),"Move Left\t") >>= ok where            ok p = writer ((),"Go\t") >>  return p

增加辅助函数,做最终转换

instance (Monoid w) => Monad (Writer w) where    return x = Writer (x,mempty)    (Writer (x,v)) >>= f = let (Writer ( y,v')) = fx        in Writer (y, v `mappend` v')
temp ::Int -> Writer String Inttemp i = writer ((),"GO\t") >> return (i-1)move i =  case runWriter(temp i) of           (y,v') -> writer (y, "Move Left\t" `mappend` v')           _ -> writer(0,mempty)

对于temp的模拟,可以参看monad这里的对于>>的实现,确实是使用了>>=。这样既满足了需要串行运算上下文,又能保证丢弃了上文的M a的a,但是仍然可以把M传递给下文。而恰好,Writer的声明形式是Writer String a,这样可以把a丢弃掉,而把String传递给下文,达到了tell要求的仅保留String内容,而忽略a的内容.
结论:
可以看出,上下文首先是因为做了内部的do运算,得到的关键的Writer((),"Go")
之后把(i-1) 附属到了tell的结果上,然后再做外部运算,结束掉x <- left i

0 0
原创粉丝点击