haskell - 哈斯克尔。如何在纯 Haskell 函数中进行 IO?执行函数时如何打印中间结果?
问题描述
在 Java、C 或 Python 等许多命令式编程语言中,我们可以轻松添加打印函数,该函数可以为我们提供有关程序中间状态的信息。
我的目标是找到一种在 Haskell 中做类似事情的方法。我想要一个不仅计算值而且打印一些东西的函数。下面的函数是我想要做的简化版本。我的实际功能过于复杂和不全面,没有上下文,无法在这里展示。
我的想法是有一个“纯”的 Haskell 函数,里面有一个辅助函数,里面有[Int] -> IO () -> Int
类型签名。IO 参数在where
子句中初始化为do
块。但不幸的do
是,当我在 GHCI 中运行该函数时,该块没有被执行。该函数已成功编译
module Tests where
-- Function returns the sum of the list and tries to print some info
-- but no IO actually happens
pureFuncWithIO :: [Int] -> Int
pureFuncWithIO [] = 0
pureFuncWithIO nums = auxIOfunc nums (return ())
where
auxIOfunc [] _ = 0
auxIOfunc (n : ns) _ = n + auxIOfunc ns (sneakyIOaction n)
sneakyIOaction n
= do -- Not executed
putStrLn $ "adding " ++ (show n);
return ()
GHCI 测试中的输出:
*Tests> pureFuncWithIO [1,2,3,4,5]
15
同时,我期待这样的事情:
*Tests> pureFuncWithIO [1,2,3,4,5]
adding 1
adding 2
adding 3
adding 4
adding 5
15
是否有可能想出一种方法来拥有IO
内部,保持最外层函数的返回类型,而不是一种IO a
味道?谢谢!
解决方案
这种类型的签名
pureFuncWithIO :: [Int] -> Int
向调用者承诺不会观察到任何副作用(如打印)。编译器将拒绝任何执行 IO 的尝试。调试存在一些例外 ( Debug.Trace
),但它们并不打算留在生产代码中。还有一些“被禁止的”、不安全的低级函数永远不应该在常规代码中使用——你应该假装它们根本不存在。
如果你想做 IO,你需要一个 IO 返回类型。
pureFuncWithIO :: [Int] -> IO Int
这样做可以将副作用与其余代码交织在一起。
pureFuncWithIO [] = return 0
pureFuncWithIO (n : ns) = do
putStrLn $ "adding " ++ show n
res <- pureFuncWithIO ns
return (n + res)
Haskell 设计的一个重点是严格区分不能做 IO 和能做 IO 的功能。在非 IO 上下文中执行 IO 是 Haskell 类型系统旨在防止的。
推荐阅读
- json - 在控制台参数中将字符串化的 json 显示为树
- sql - Github 不会更改我们团队 django 环境中的数据库
- java - 如何使用Java8并行流循环一个大向量
- angular - 演示组件输入不响应可观察的切换?
- python - 多参数函数不适用于数据框
- reactjs - Fontawesome 5 React 品牌图标不起作用 - 如何解决?
- c++ - 如何在CUDA中嵌套for循环?
- elasticsearch - 弹性搜索查询返回匹配数组中所有元素的文档
- python - 模式匹配从字符串中获取列表和字典
- php - 为什么 print_r 会重置 mysqli 对象?