【问题标题】:Haskell - Print a Trace After ExecutionHaskell - 执行后打印跟踪
【发布时间】:2012-04-26 23:23:51
【问题描述】:

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

输入函数时打印消息很容易,我只是使用Debug.trace,例如:

functionValue = trace "Entering function" (evaluateFunction functionArguments)

分配给变量时也适用相同的过程。我想不通的是如何在从函数调用返回时打印并使输出与其他输出正确计时。到目前为止,我所做的每一次尝试都导致在“输入函数”之后立即打印“离开函数” - 我需要在打印“离开函数”之前打印函数的内部调试语句(分配和嵌套函数调用)。

我的命令式习惯告诉我,我需要一种在离开函数输出之前强制执行 (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...

那么,“运行 myFunctionWithTraces 然后打印 myString”的 Haskell 习语是什么?

【问题讨论】:

  • 由于您需要保证消息按特定顺序打印,因此不应使用 Debug.Trace。相反,咬紧牙关,在 IO 中运行,并使用 putStrLn 或类似名称。
  • 你在编译到 Haskell 吗?

标签: haskell io monads trace side-effects


【解决方案1】:

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

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

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

上面的 sn-p 会返回 IO()(空的 IO monad)。所以我修改为:

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

但现在正在打印:

内部函数操作... ... ... 输入功能 离开功能

编辑:错误的输出是我的错:我不小心把result &lt;- f 之前 putStrLn "Entering...

【讨论】:

    【解决方案2】:

    如果要立即打印trace,可以将函数提升到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 ()
    

    这也可以纯粹地使用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
    

    【讨论】:

      猜你喜欢
      • 2015-07-27
      • 1970-01-01
      • 2013-10-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-25
      • 2018-07-06
      相关资源
      最近更新 更多