Haskell - 执行后打印跟踪

标签 haskell io monads trace side-effects

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

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

functionValue = trace "Entering function" (evaluateFunction functionArguments)

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

我的命令式习惯告诉我,我需要一种方法在 left-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...

那么,“run myFunctionWithTraces then print myString”的 Haskell 习惯用法是什么?

最佳答案

如果你想立即打印痕迹,你可以将函数提升到 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

关于Haskell - 执行后打印跟踪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10160436/

相关文章:

haskell - 它不是单子(monad),但它是什么?

haskell - 为什么应用仿函数可以有副作用,但仿函数不能?

haskell - 任何人都知道如何使用部分应用的三参数函数中缀(haskell)

c++ - 如何提高文件读取速度?

c++ - 解析 Google Protocol Buffer 的文本文件

java - 写入文件时出现 IndexOutOfBoundException

haskell - 将错误值提升到 ErrorT monad 转换器

parsing - 表达式树的缩进感知解析

multithreading - Haskell forkIO 线程使用 putStrLn 在彼此之上写入

haskell - 无法从上下文错误中推断出包含的约束