如何将 `IO ()` 传递给 `main` 才能被认为是纯粹的?

问题描述 投票:0回答:1

我不太明白打印(输出到屏幕)在编程语言意义上如何被认为是纯粹的,但我声称 Haskell 中存在这样的概念。

您通过

IO ()
创建一个
print
对象,然后将其传递给
main
,它会为您进行打印。

我的问题:调用

main
来执行如此不纯粹的行为本身怎么能被认为是纯粹的呢?即使您声称
main
存在于您的 Haskell 程序之外,您仍然调用它来执行副作用。

haskell io programming-languages pure-function
1个回答
0
投票

从您的评论来看,我认为困惑的关键点是:

main
的定义在代码执行期间不会产生副作用吗?

它没有!定义

main
没有任何副作用。 –好吧,在懒惰的 Haskell 中定义某些东西通常没有多大意义,而且强制
main
动作为正常形式
不会有任何副作用:

ghci> let sideEffect :: IO (); sideEffect = putStrLn "I'M HAVING AN EFFECT!"
ghci> sideEffect `seq` 0
0

看,没有打印任何内容。

(这个演示并没有那么有意义;

seq
只强制使用弱头范式,并且如果编译器可以证明这不会改变严格性属性的任何内容,实际上可能根本不会强制执行任何操作。但是实际论证成立。)

副作用的发生与 Haskell 定义中发生的纯粹的事情完全分开。正如已经说过的,你所做的纯粹是“定义一个配方”来说明副作用应该如何发生。这并不意味着它们真的会发生。这就像一个罪犯在监狱里策划抢劫银行一样:他可以随心所欲地策划邪恶的事情,但实际上什么也不会发生,因为他无法离开监狱。

运行包含此

main操作的程序是一个完全不同的故事。现在我们的食谱提到的任何副作用都会释放到世界各地。

您现在可能会问:这在其他编程语言中不也是一样的吗?毕竟,用 C 或 Java 编译程序也不会产生副作用,只有“运行编译后的程序”才会产生副作用。

不同之处在于,在 Haskell 中,您实际上可以评估函数并进行计算,同时仍然可以确保不会发生副作用。这很方便,因为您可以在 GHCi 中尝试一些东西,或者对各个函数进行单元测试,或者使用处理用户定义代码的 Web 框架,并确保没有副作用或对全局状态的依赖会混淆结果。

通过相比之下,在 C 中,当评估一个函数(您只知道其签名)时,您永远无法知道它是否还会向终端打印某些内容或连接到远程服务器或诸如此类。 (好吧,你可以将其沙箱化,但这很粗糙且笨拙。) 当然,如果你把
unsafePerformIO
带入图片中,一切都会崩溃:有了它,Haskell定义

can

也会产生副作用。

ghci> :m +System.IO.Unsafe 
ghci> let unsafeSideEffect :: (); unsafeSideEffect = unsafePerformIO (putStrLn "I'M HAVING AN EFFECT!")
ghci> unsafeSideEffect `seq` 0
I'M HAVING AN EFFECT!
0

但我不认为 

unsafePerformIO
 真的是 Haskell 语言的一部分;它是一个逃生舱口,主要用于外部函数调用和低级优化。


© www.soinside.com 2019 - 2024. All rights reserved.