Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
我如何利用haskell的州和作家?_Haskell_Monads - Fatal编程技术网

我如何利用haskell的州和作家?

我如何利用haskell的州和作家?,haskell,monads,Haskell,Monads,当我阅读了LYAH的最后一章并与之会面时,我给自己分配了一个任务,使其成为状态单子,以便源代码看起来更清晰,如: manipList = do goForward goForward goBack 同时,我想利用Writer monad为这个过程保留一个日志,但我不知道如何将这两个monad组合在一起 我的解决方案是在状态中保留一个[String],我的源代码是 import Control.Monad import Control.Monad.State type

当我阅读了LYAH的最后一章并与之会面时,我给自己分配了一个任务,使其成为状态单子,以便源代码看起来更清晰,如:

manipList = do
    goForward
    goForward
    goBack
同时,我想利用Writer monad为这个过程保留一个日志,但我不知道如何将这两个monad组合在一起

我的解决方案是在状态中保留一个[String],我的源代码是

import Control.Monad
import Control.Monad.State

type ListZipper a = ([a], [a])

-- move focus forward, put previous root into breadcrumbs
goForward :: ListZipper a -> ListZipper a
goForward (x:xs, bs) = (xs, x:bs)

-- move focus back, restore previous root from breadcrumbs
goBack :: ListZipper a -> ListZipper a
goBack (xs, b:bs) = (b:xs, bs)

-- wrap goForward so it becomes a State
goForwardM :: State (ListZipper a) [a]
goForwardM = state stateTrans where
    stateTrans z = (fst newZ, newZ) where
        newZ = goForward z

-- wrap goBack so it becomes a State
goBackM :: State (ListZipper a) [a]
goBackM = state stateTrans where
    stateTrans z = (fst newZ, newZ) where
        newZ = goBack z

-- here I have tried to combine State with something like a Writer
-- so that I kept an extra [String] and add logs to it manually

-- nothing but write out current focus
printLog :: Show a => State (ListZipper a, [String]) [a]
printLog = state $ \(z, logs) -> (fst z, (z, ("print current focus: " ++ (show $ fst z)):logs))

-- wrap goForward and record this move
goForwardLog :: Show a => State (ListZipper a, [String]) [a]
goForwardLog = state stateTrans where
    stateTrans (z, logs) = (fst newZ, (newZ, newLog:logs)) where
        newZ = goForward z
        newLog = "go forward, current focus: " ++ (show $ fst newZ)

-- wrap goBack and record this move
goBackLog :: Show a => State (ListZipper a, [String]) [a]
goBackLog = state stateTrans where
    stateTrans (z, logs) = (fst newZ, (newZ, newLog:logs)) where
        newZ = goBack z
        newLog = "go back, current focus: " ++ (show $ fst newZ)

-- return
listZipper :: [a] -> ListZipper a
listZipper xs = (xs, [])

-- return
stateZipper :: [a] -> (ListZipper a, [String])
stateZipper xs = (listZipper xs, [])

_performTestCase1 = do
    goForwardM
    goForwardM
    goBackM

performTestCase1 =
    putStrLn $ show $ runState _performTestCase1 (listZipper [1..4])

_performTestCase2 = do
    printLog
    goForwardLog
    goForwardLog
    goBackLog
    printLog

performTestCase2 = do
    let (result2, (zipper2, log2)) = runState _performTestCase2 $ stateZipper [1..4]
    putStrLn $ "Result: " ++ (show result2)
    putStrLn $ "Zipper: " ++ (show zipper2)
    putStrLn "Logs are: "
    mapM_ putStrLn (reverse log2)
但问题是,我认为这不是一个好的解决方案,因为我必须手动维护日志。有没有其他方法可以混合State monad和Writer monad以便它们可以一起工作?

您正在寻找。基本思想是定义一个类型,如
WriterT
,它接受另一个monad并将其与
Writer
组合,创建一个新类型(如
WriterT log(states s)

注意:有一种惯例,变压器类型以大写字母
T
结尾。因此,
Maybe
Writer
是正常的单子,
MaybeT
WriterT
是它们的转换器等价物

其核心思想非常简单:对于一组单子,您可以很容易地想象将它们的行为结合在一起。最简单的例子是
可能
。回想一下,
可能
所做的一切就是在绑定时传播
无内容

Nothing >>= f = Nothing
Just x >>= f = f x
因此,很容易想象用这种行为扩展任何monad。我们所要做的就是先检查
Nothing
,然后使用旧的monad绑定。
MaybeT
类型正是这样做的:它包装了一个现有的monad,并在每个绑定的前面加上这样的检查。您还必须实现
return
,方法是将值包装在
中,然后使用内部monad的
return
。还有更多的管道使一切正常工作,但这是一个重要的想法

您可以想象
Writer
的一个非常类似的行为:首先我们组合任何新的输出,然后使用旧的monad绑定。这本质上就是
WriterT
的行为。其中还涉及一些其他细节,但基本思想相当简单和有用


单子变形金刚是一种非常常见的方式来“组合”你想要的单子。有一些版本的最常用的monad作为转换器,除了
IO
之外,它必须始终位于monad堆栈的底部。在您的情况下,
WriterT
StateT
都存在,并且可以用于您的程序。

Tikhon Jelvis用monad transformers给出了一个很好的答案。然而,也有一个快速的解决方案


mtl
中的模块导出
RWS
monad,它是
读卡器
写卡器
状态
monad的组合。

这正是我想要的!我试过了,他们两个(WriterT和StateT)都很好,泰!