Haskell - 执行后打印跟踪

时间:2012-04-15 08:01:09

标签: haskell io monads trace side-effects

我有一个让Uni编写一个编译器(在Haskell中)的项目,用于编写简单的命令式语言。其中一个要求是在进入函数调用时打印调试语句,保留函数并分配变量。

输入功能时打印信息很简单,我只使用Debug.trace,例如:

functionValue = trace "Entering function" (evaluateFunction functionArguments)

分配给变量时应用相同的过程。我无法弄清楚当从函数调用返回时如何打印并且输出与其他输出正确地计时。到目前为止,我所做的每一次尝试都导致了“离开”功能"在"输入功能"之后立即打印 - 我需要在" Leaving function"之前打印函数的内部调试语句(赋值和嵌套函数调用)。打印出来。

我的命令习惯告诉我在离开函数输出之前需要一种强制执行(evaluateFunction functionArguments)的方法,但这在Haskell中似乎是不可能和错误的。

我现在得到的示例输出:

Entering main function...
Leaving main function...
Entering fn1 function...
Leaving fn1 function...
Assigning value1 to A.
Assigning value2 to C.
Entering fn2 function...
Leaving fn2 function...
Assigning value3 to B.
Assigning value4 to C.

相同的程序以我需要的方式输出:

Entering main function...
Entering fn1 function...
Assigning value1 to A.
Leaving fn1 function...
Assigning value2 to C.
Entering fn2 function...
Assigning value3 to B.
Assigning value4 to C.
Leaving fn2 function...
Leaving main function...

那么,Haskell的成语是什么?运行myFunctionWithTraces然后打印myString'?

2 个答案:

答案 0 :(得分:5)

如果要立即打印迹线,可以将功能提升到IO monad,并将其置于两个putStr之间,例如

trace :: String -> IO () -> IO ()
trace name f = do
    putStrLn $ "Entering " ++ name
    f
    putStrLn $ "Leaving " ++ name

然后:

main = trace "main" $ do
    fn1
    fn2

fn1 = trace "fn1" $ do
    return ()

fn2 = trace "fn2" $ do
    return ()

这也可以完全用Writer monad完成(即不打印,但只是累积调试输出)。 trace看起来更像是这样:

trace :: String -> Writer String () -> Writer String ()
trace name f = do
    tell $ "Entering " ++ name ++ "\n"
    f
    tell $ "Leaving " ++ name ++ "\n"

以及使用runWriterexecWriter展开调试输出的附加步骤。

修改:将trace推广到IO a并不太难:

trace :: String -> IO a -> IO a
trace name f = do
    putStrLn $ "Entering " ++ name
    ret <- f
    putStrLn $ "Leaving " ++ name
    return ret

main = trace "main" $ do
    a <- fn1
    b <- fn2
    print $ a + b

fn1 = trace "fn1" $ do
    return 42

fn2 = trace "fn2" $ do
    return 69

答案 1 :(得分:1)

[代码在评论中不可读,所以我发布了另一个答案作为对Cat Plus Plus的评论]

我终于通过我的所有代码对IO monad进行了线程化,但是这个解决方案并不是很有效 - 我需要从函数中获取返回值(即IO(值))。

do
    putStrLn $ "Entering " ++ name
    f
    putStrLn $ "Leaving " ++ name

上面的代码片段将返回IO()(空IO monad)。所以我把它修改为:

do
    putStrLn $ "Entering " ++ name
    returnVal <- f
    putStrLn $ "Leaving " ++ name
    return (returnVal)

<击> 但现在打印:

内部功能动作...... ... ... 输入功能 离开功能

编辑:输出错误是我的错:我不小心将result <- f 放在 putStrLn "Entering...

之前