2

I have a project for Uni to write a compiler (in Haskell) for a simple made-up imperative language. One of the requirements is printing debug statements on entering a function call, leaving a function and assigning variables.

Printing messages when entering functions is easy, I just use Debug.trace, eg:

functionValue = trace "Entering function" (evaluateFunction functionArguments)

The same process applies when assigning to variables. What I can't figure out is how to print when returning from a function call and have the output timed correctly with the other outputs. Every attempt I've made so far has resulted in "Leaving function" being printed immediately after "Entering function" - I need the function's internal debug statements (assigning and nested function calls) to be printed before "Leaving function" is printed.

My imperative habits tell me that I need a way to force execution of (evaluateFunction functionArguments) before the leave-function output, but this seems impossible and wrong in Haskell.

Example output I get now:

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.

Same program's output the way I need it to look:

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...

So, what's Haskell idiom for 'run myFunctionWithTraces then print myString'?

4

2 回答 2

5

如果要立即打印痕迹,可以将函数提升到 IO monad,并将其放在两个putStrs 之间,例如

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 ()

这也可以纯粹地用Writermonad 来完成(即不打印,而只是在你去的时候积累调试输出)。trace然后看起来更像这样:

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

以及使用runWriter或展开调试输出的附加步骤execWriter

编辑:概括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
于 2012-04-15T08:42:04.980 回答
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)

但这现在正在打印:

inside function actions... ... ... 进入函数 离开函数

编辑:不正确的输出是我的错:我不小心result <- f putStrLn "Entering...

于 2012-04-22T18:25:51.423 回答