强制 IO 评估

t4c*_*cer 0 haskell

我需要一些包装在 IO 类型上,但我不能“强制”haskell 在包装器中运行 IO 操作。我尝试为它使用 bang 模式,但它也不起作用。

我在更简单的类型上重新创建了问题。下面的代码片段

{-# LANGUAGE BangPatterns #-}

newtype IOWrapper a = IOWrapper (IO a) 

mkWrapper :: IO a -> IOWrapper a
mkWrapper !a = IOWrapper a

foo :: IO ()
foo = putStrLn "foo" --Shows "foo" on console, as expected

bar :: IOWrapper ()
bar = IOWrapper $ putStrLn "bar" --Shows nothing

baz :: IOWrapper ()
baz = mkWrapper $ putStrLn "baz" --Shows nothing
Run Code Online (Sandbox Code Playgroud)

ama*_*loy 7

你不能这样做(不作弊)。评估 IO 不会使其发挥作用。导致 IO 执行效果的唯一方法是将其包含在特殊动作中main :: IO a,通常是将多个较小的动作组合成一个大的 IO 动作。

与其问怎么做这件事,倒不如回想一下你为什么想做这件事。而是询问如何实现您最初的目标。


Fyo*_*kin 6

仅仅调用putStrLn "foo"实际上并不打印“foo”。相反,它创建了一个“动作”,然后必须单独“执行”,然后才会打印“foo”。而且,你实际上可以多次“执行”它,效果也会产生多次。

“执行”一个IO动作的唯一方法是让它成为入口点——即main函数——或者入口点的一部分,或者另一个函数的一部分,它本身就是入口点的一部分,等等。入口点是所有效果的根,在它之外没有效果执行。

您的foo函数可能看起来“有效”,因为您在 GHCi 中运行它,对吗?如果是这样,那么它会作为 GHCi 自己的入口点的一部分执行。GHCi 识别IO类型的事物并立即执行它们。

但是 GHCi 不识别该IOWrapper类型的东西,所以它不会解包和执行它们。

所以你唯一的办法就是解开并执行自己:

runIOWrapper :: IOWrapper a -> IO a
runIOWrapper (IOWrapper a) = a
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样在 GHCi 中运行你的包装动作:

> runIOWrapper bar
bar

> runIOWrapper baz
baz
Run Code Online (Sandbox Code Playgroud)