A_b*_*lop 4 debugging logging functional-programming purescript visual-studio-code
以下是一个最小的、人为的示例:
read :: FilePath -> Aff String
read f = do
log ("File: " <> f) -- (1)
readTextFile UTF8 f -- (2)
Run Code Online (Sandbox Code Playgroud)
我想在发生(1)潜在错误之前进行一些调试日志记录(2)。到目前为止,在 Spago REPL 中执行以下代码适用于成功案例:
$ spago repl
> launchAff_ $ read "test/data/tree/root.txt"
File: test/data/tree/root.txt
unit
Run Code Online (Sandbox Code Playgroud)
(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'
}
Run Code Online (Sandbox Code Playgroud)
原来的问题更复杂,包括多层递归(请参阅电子书练习 3),我需要记录日志来调试上述错误。
无论这里即将出现错误,我如何正确记录?
首先,您观察到的症状并不意味着第一行不执行。它总是执行,只是由于控制台在 PureScript REPL 中的工作方式,您看不到它的输出。输出被吞噬。遗憾的是,这并不是 REPL 的唯一问题。
\nlog您可以通过替换为throwError并观察总是抛出错误来验证第一行是否始终执行。或者,您可以使第一行修改可变单元格,而不是写入控制台,然后检查单元格的内容。
最后,这只发生在 REPL 中。如果您将该launchAff_调用放入内部main并运行该程序,您将始终获得控制台输出。
现在讨论手头的实际问题:如何调试跟踪。
\n如果你能负担得起的话,记录到控制台就可以了,但是还有一种更优雅的方法:Debug.trace.
这个函数有一个隐藏的效果——即它的类型表明它是纯粹的,但在调用时它确实产生了效果。这个小谎言让您可以trace在纯设置中使用,从而调试纯代码。不需要Effect!只要仅用于调试就可以,但不要将其放入生产代码中。
它的工作方式是它需要两个参数:第一个参数被打印到控制台,第二个参数是打印后要调用的函数,整个结果就是该函数返回的结果。例如:
\ncalculateSomething :: Int -> Int -> Int\ncalculateSomething x y =\n trace ("x = " <> show x) \\_ ->\n x + y\n\nmain :: Effect Unit\nmain =\n log $ show $ calculateSomething 37 5\n\n> npx spago run \n\'x = 37\' \n42 \nRun Code Online (Sandbox Code Playgroud)\n第一个参数可以是任何东西,而不仅仅是字符串。这可以让您轻松打印很多东西:
\ncalculateSomething :: Int -> Int -> Int\ncalculateSomething x y =\n trace { x, y } \\_ ->\n x + y\n\n> npx spago run\n{ x: 37, y: 5 }\n42\nRun Code Online (Sandbox Code Playgroud)\n或者,将其应用到您的代码中:
\nread :: FilePath -> Aff String\nread f = trace ("File: " <> f) \\_ -> do\n readTextFile UTF8 f\nRun Code Online (Sandbox Code Playgroud)\n但这里有一个微妙的细节:一旦您调用 read,此跟踪就会发生,即使结果Aff永远不会真正执行。如果您需要在有效执行时进行跟踪,则需要将调用作为trace操作的一部分,并注意不要将其作为序列中的第一个操作:
read :: FilePath -> Aff String\nread f = do\n pure unit\n trace ("File: " <> f) \\_ -> pure unit\n readTextFile UTF8 f\nRun Code Online (Sandbox Code Playgroud)\n当然,每次需要在有效的上下文中进行跟踪时执行此操作有点不方便,因此有一个特殊的函数可以为您完成此操作 - 它被称为traceM:
read :: FilePath -> Aff String\nread f = do\n traceM ("File: " <> f)\n readTextFile UTF8 f\nRun Code Online (Sandbox Code Playgroud)\n如果您查看其源代码,您会发现它的作用与我在上面的示例中所做的完全相同。
\n可悲的是,trace当异常发生时,它不会在 REPL 中帮助你,因为它仍然打印到控制台,所以它仍然会因为同样的原因被吞没。
但即使它没有被吞没,输出也会有点乱码,因为trace实际上以颜色输出(以帮助您在其他输出中区分出来),并且 PureScript REPL 与颜色有复杂的关系:
> calculateSomething 37 5\n\xe2\x86\x90[32m\'x = 37\'\xe2\x86\x90[39m\n42\nRun Code Online (Sandbox Code Playgroud)\n
除了Fyodor Soikin 的精彩答案之外,我还发现了一个使用 VS Code 调试视图的变体。
1.) 确保使用源映射进行构建:
spago build --purs-args "-g sourcemaps"
Run Code Online (Sandbox Code Playgroud)
2.) 将调试配置添加到 VS Code launch.json:
spago build --purs-args "-g sourcemaps"
Run Code Online (Sandbox Code Playgroud)
将"./output/Main/index.js"/替换为要调试的.main()编译文件/函数。.js
3.) 设置断点并.purs通过源映射支持单步执行文件。