7

在 Haskell 中是print一个纯函数;为什么或者为什么不?我认为不是,因为它并不总是返回与纯函数应该返回的值相同的值。

4

3 回答 3

9

type 的值IO Int并不是真正的Int. 它更像是一张纸,上面写着“嘿,Haskell 运行时,请以Int这样那样的方式产生一个值”。这张纸是惰性的并且保持不变,即使Int运行时最终产生的 s 是不同的。

您可以通过将这张纸分配给main. 如果该IO操作从不妨碍main某个容器,而是在某个容器内停滞不前,那么它将永远不会被执行。

返回IO动作的函数和其他函数一样是纯函数。他们总是返回同一张纸。运行时如何处理这些指令是另一回事。

如果它们不纯,我们将不得不在改变之前三思而后行

foo :: (Int -> IO Int) -> IO Int
foo f = liftA2 (+) (f 0) (f 0)

至:

foo :: (Int -> IO Int) -> IO Int
foo f = let x = f 0 in liftA2 (+) x x
于 2017-11-16T21:13:22.757 回答
1

如果您只是阅读纯函数的标签(一个在给定相同参数值的情况下始终评估为相同结果值并且不会导致任何语义上可观察的副作用或输出的函数,例如可变对象或输出的突变到 I/O 设备。)然后在打印类型中思考:

putStrLn :: String -> IO ()

你会在那里找到一个技巧,它总是返回IO (),所以......,它会产生效果。所以在引用透明度方面不是纯例如getLinereturnsIO String但它也是一个纯函数。(@interjay 贡献),我想说的是,答案非常接近问题:

在价值问题上,对于相同的输入,IO ()将始终是相同的值。IO ()

关于执行,它不是纯粹的,因为执行 IO ()可能会产生副作用(在屏幕上放一个字符串,在这种情况下看起来很无辜,但是一些 IO 可以午餐核弹,然后返回 Int 42)

您可以在这里使用@Ben 的好方法更好地理解:

“有几种方法可以解释你如何“纯粹”操纵现实世界。一种是说 IO 就像一个状态单子,只有被线程化的状态是你程序之外的整个世界;=(所以你的Stuff -> IO DBThing 函数确实有一个额外的隐藏参数,它接收世界,实际上返回一个 DBThing 以及另一个世界;它总是被不同的世界调用,这就是为什么即使使用相同的 Stuff 调用它也可以返回不同的 DBThing 值). 另一种解释是 IO DBThing 值本身就是一个命令式程序;你的 Haskell 程序是一个完全不做 IO 的纯函数,它返回一个做 IO 的不纯程序,而 Haskell 运行时系统(不纯)执行它返回的程序。”

和@Erik Allik:

因此,返回 IO a 类型值的 Haskell 函数实际上并不是在运行时执行的函数——被执行的是 IO a 值本身。所以这些函数实际上是纯的,但它们的返回值表示非纯计算。

你可以在这里找到它们用 IO 理解 Haskell 中的纯函数

于 2017-11-16T18:00:14.553 回答
1

是的,print是一个纯函数。它返回的值具有 type IO (),您可以将其视为输出您传入的字符串的一堆代码。对于您传入的每个字符串,它总是返回相同的代码。

于 2017-11-16T21:51:33.067 回答