哈斯克尔。如何在纯 Haskell 函数中进行 IO?如何在执行函数时打印中间结果?

And*_*Uho 1 io haskell

在 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 () 
Run Code Online (Sandbox Code Playgroud)

GHCI 测试中的输出:

*Tests> pureFuncWithIO [1,2,3,4,5]
15
Run Code Online (Sandbox Code Playgroud)

同时,我期待这样的事情:

*Tests> pureFuncWithIO [1,2,3,4,5]
adding 1
adding 2
adding 3
adding 4
adding 5
15
Run Code Online (Sandbox Code Playgroud)

是否有可能想出一种方法来IO保持最外层函数的返回类型,而不是一种IO a味道?谢谢!

chi*_*chi 6

这种类型的签名

pureFuncWithIO :: [Int] -> Int
Run Code Online (Sandbox Code Playgroud)

看好给呼叫者没有副作用(如印刷)将观察到。编译器将拒绝任何执行 IO 的尝试。调试 ( Debug.Trace)存在一些例外情况,但它们并不意味着留在生产代码中。还有一些“禁止”的、不安全的低级函数,它们永远不应该在常规代码中使用——你应该假装这些根本不存在。

如果要做IO,就需要IO返回类型。

pureFuncWithIO :: [Int] -> IO Int
Run Code Online (Sandbox Code Playgroud)

这样做允许与其余代码编织副作用。

pureFuncWithIO []       = return 0
pureFuncWithIO (n : ns) = do
   putStrLn $ "adding " ++ show n
   res <- pureFuncWithIO ns
   return (n + res)
Run Code Online (Sandbox Code Playgroud)

Haskell 设计的一个重点是严格区分不能做 IO 的功能和可以做 IO 的功能。在非 IO 上下文中执行 IO 是 Haskell 类型系统旨在防止的。