所以我正在建立一个State Monad并遇到一些懒惰的问题,这让我很难调试.
我的州Monad通过列入一个值列表来操作,将它们一个接一个地推送到州的一部分,然后我分析每个州之后的状态值,以产生该州的另一部分.
我想出了这个简单的例子来说明为什么它很难调试.
module Main where
import Control.Monad.State
import Debug.Trace
runSim :: [Int] -> State String String
runSim [] = return =<< get
runSim (cur:xs) = do
lst <- get
let val = (show $ 2*cur)
put $ trace ((show $ length lst) ++ " " ++ (show cur)) ((val) ++ "," ++ lst)
runSim xs
main :: IO ()
main = print $ evalState (runSim [1..10]) ""
Run Code Online (Sandbox Code Playgroud)
这个输出是:
0 1
2 2
4 3
6 4
8 5
11 6
14 7
17 8
20 9
23 10
"20,18,16,14,12,10,8,6,4,2,"
Run Code Online (Sandbox Code Playgroud)
但是,如果我将跟踪线更改为:
put $ trace ((show cur)) ((val) ++ "," ++ lst)
Run Code Online (Sandbox Code Playgroud)
输出反转:
10
9
8
7
6
5
4
3
2
1
"20,18,16,14,12,10,8,6,4,2,"
Run Code Online (Sandbox Code Playgroud)
但最终结果是一样的.有没有更好的方法来处理状态Monad在调试中的懒惰,所以它更自然地顺序?
问题是trace呼叫只在最后进行评估.
计算建立状态(为了简洁起见仅列出两个元素)
runSim [1, 2] "" ~> ( (), state1@(trace (output 1 "") (logString 1 "")))
~> runSim [2] ( (), trace (output 2 state1) (logString2 state1))
Run Code Online (Sandbox Code Playgroud)
所以在最终状态中,trace最后一个推送列表元素是最外层的.
现在在第二种情况下,在哪里
output i _ = show i
Run Code Online (Sandbox Code Playgroud)
跟踪输出不依赖于之前发生的事情,因此trace推送的最后一个是先运行等.
但在第一种情况下,在哪里
output i state = show (length state) ++ " " ++ show i
Run Code Online (Sandbox Code Playgroud)
跟踪输出取决于状态,因此必须在打印跟踪输出之前评估状态.但是state是对先前推送的调用trace,因此跟踪需要先运行等等.因此跟踪输出中的数据依赖性确保跟踪按推送顺序运行.
要确保跟踪以该顺序运行而没有数据依赖性,您必须将trace调用拉出put或强制评估put状态,
put $! trace ((show $ length lst) ++ " " ++ (show cur)) ((val) ++ "," ++ lst)
Run Code Online (Sandbox Code Playgroud)
要么
trace ((show $ length lst) ++ " " ++ (show cur)) $ put ((val) ++ "," ++ lst)
Run Code Online (Sandbox Code Playgroud)