【问题标题】:How to debug with PureScript?如何使用 PureScript 进行调试?
【发布时间】:2020-12-29 12:41:00
【问题描述】:

问题

以下是一个最小的、人为的例子:

read :: FilePath -> Aff String
read f = do
  log ("File: " <> f) -- (1)
  readTextFile UTF8 f -- (2)

我想在(1) 中进行一些调试日志记录,然后在(2) 上发生潜在错误。到目前为止,在 Spago REPL 中执行以下代码适用于成功案例:

$ spago repl
> launchAff_ $ read "test/data/tree/root.txt"
File: test/data/tree/root.txt
unit

问题:如果(2) 出现错误 - file is directory here - ,(1) 似乎根本没有执行:

$ spago repl
> launchAff_ $ read "test/data/tree"
~/purescript-book/exercises/chapter9/.psci_modules/node_modules/Effect.Aff/foreign.js:532
                throw util.fromLeft(step);
                ^

[Error: EISDIR: illegal operation on a directory, read] {
  errno: -21,
  code: 'EISDIR',
  syscall: 'read'
}

最初的问题更复杂,包括几层递归(请参阅E-Book exercise 3),我需要记录以调试上述错误。

问题

  1. 我如何才能正确记录这里即将出现的错误?
  2. (可选)是否有更复杂、完善的调试替代方案 - purescript-debugger?精心设计的 VS Code 调试扩展/功能将是锦上添花。

【问题讨论】:

  • 我不确定这是否有帮助,但在任何 monadic (do) 或 applicative (ado) 块中,您可以尝试traceM from:pursuit.purescript.org/packages/purescript-debug/4.0.0/docs/…。我经常使用它;-)
  • @paluh 感谢您的提示,我猜您的思路是正确的(Spago REPL 也存在问题)。请参阅 Fyodor Soikin 的答案。

标签: debugging logging visual-studio-code functional-programming purescript


【解决方案1】:

首先,您观察到的症状并不意味着第一行没有执行。它总是执行,由于控制台在 PureScript REPL 中的工作方式,您只是看不到它的输出。输出被吞没。遗憾的是,这不是 REPL 的唯一问题。

您可以通过将log 替换为throwError 并观察始终抛出错误来验证第一行是否始终执行。或者,您可以让第一行修改可变单元格而不是写入控制台,然后检查单元格的内容。

最后,这只发生在 REPL 中。如果将 launchAff_ 调用放在 main 中并运行程序,您将始终获得控制台输出。


现在回到手头的实际问题:如何调试跟踪。

如果您负担得起,登录到控制台是可以的,但还有一种更优雅的方式:Debug.trace

这个函数有一个隐藏的效果 - 即它的类型说它是纯的,但它在调用时确实会产生效果。这个小谎言让您可以在纯设置中使用trace,从而调试纯代码。不需要Effect!只用于调试就可以,但不要放在生产代码中。

它的工作方式是它需要两个参数:第一个被打印到控制台,第二个是打印后调用的函数,整个事情的结果就是该函数返回的任何内容。例如:

calculateSomething :: Int -> Int -> Int
calculateSomething x y =
    trace ("x = " <> show x) \_ ->
        x + y

main :: Effect Unit
main =
  log $ show $ calculateSomething 37 5

>  npx spago run       
'x = 37'               
42                     

第一个参数可以是任何东西,而不仅仅是一个字符串。这让您可以轻松打印很多东西:

calculateSomething :: Int -> Int -> Int
calculateSomething x y =
    trace { x, y } \_ ->
        x + y

>  npx spago run
{ x: 37, y: 5 }
42

或者,将其应用于您的代码:

read :: FilePath -> Aff String
read f = trace ("File: " <> f) \_ -> do
  readTextFile UTF8 f

但这里有一个微妙的细节:只要您调用read,这种跟踪就会发生,即使结果Aff 永远不会真正执行。如果您需要在有效执行时进行跟踪,则需要将 trace 调用作为操作的一部分,并注意不要将其作为序列中的第一个操作:

read :: FilePath -> Aff String
read f = do
  pure unit
  trace ("File: " <> f) \_ -> pure unit
  readTextFile UTF8 f

当然,每次您需要在有效的上下文中进行跟踪时都执行此操作有点不方便,因此有一个特殊功能可以为您执行此操作 - 它称为 traceM

read :: FilePath -> Aff String
read f = do
  traceM ("File: " <> f)
  readTextFile UTF8 f

如果您查看它的source code,您会发现它与我在上面的示例中所做的完全一样。


可悲的是,当异常发生时,trace 不会在 REPL 中为您提供帮助,因为它仍在打印到控制台,因此出于同样的原因它仍然会被吞没。

但即使它没有被吞下,输出也有点乱码,因为trace 实际上输出的是颜色(帮助你在其他输出中分辨出来),而 PureScript REPL 与颜色的关系很复杂:

> calculateSomething 37 5
←[32m'x = 37'←[39m
42

【讨论】:

  • 太棒了,感谢您的详尽回答!我现在明白了,问题尤其出在 Spago REPL 上。看来,这个限制有可能让新手(比如我)感到困惑,你知道是否有跟踪 github 的问题吗? tracetraceM 绝对有帮助而且更优雅!
【解决方案2】:

除了Fyodor Soikin's great answer,我发现了一个使用VS Code调试视图的变种。

1.) 确保build with sourcemaps:

spago build --purs-args "-g sourcemaps"

2.) 将调试配置添加到 VS Code launch.json:

{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "pwa-node",
      "request": "launch",
      "name": "Launch Program",
      "skipFiles": ["<node_internals>/**"],
      "runtimeArgs": ["-e", "require('./output/Main/index.js').main()"],
      "smartStep": true // skips files without (valid) source map
    }
  ]
}

"./output/Main/index.js"/.main()替换为编译后的.js要调试的文件/函数。

3.) 设置断点并通过源映射支持单步执行.purs 文件。

【讨论】:

    猜你喜欢
    • 2012-06-27
    • 2023-03-14
    • 2014-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-11
    • 2010-11-12
    • 2018-06-26
    相关资源
    最近更新 更多