我是 Haskell 编程语言的新手,我一直在将IO类型作为函数参数或返回类型绊倒。
playGame :: Screen -> IO ()
Run Code Online (Sandbox Code Playgroud)
或者
gameRunner :: IO String -> (String -> IO ()) -> Screen -> IO ()
Run Code Online (Sandbox Code Playgroud)
这是如何工作的,我有点困惑,因为我知道 String 需要单词而 Int 需要数字。IO函数中使用的什么是期望或返回?
IO是 Haskell 如何区分引用透明的代码和不透明的代码的方式。IO a是返回 .io 的 IO 操作的类型a。
您可以将 IO 操作视为对等待执行的现实世界产生某种影响的一段代码。由于这种副作用,IO 操作在引用上不是透明的;因此,执行顺序很重要。mainHaskell 程序的任务是正确排序和执行所有 IO 操作。因此,当您编写一个返回 的函数时IO a,您实际上正在编写一个返回一个操作的函数,该操作最终 - 当被执行时main- 执行该操作并返回一个a.
还有一些解释:
引用透明意味着你可以用它的值替换一个函数。引用透明的函数不能有任何副作用;特别是,引用透明的函数不能访问任何硬件资源,如文件、网络或键盘,因为函数值将取决于它的参数以外的其他东西。
像 Haskell 这样的函数式语言中的引用透明函数就像数学函数(域和域之间的映射),不仅仅是关于如何计算函数值的一系列命令式指令。因此,Haskell 代码告诉编译器一个函数被应用于它的参数,但它没有说一个函数被调用并因此被实际计算。
因此,引用透明的函数并不暗示执行的顺序。Haskell 编译器可以自由地以它认为合适的任何方式评估函数 - 或者如果没有必要则根本不评估它们(称为惰性评估)。唯一的排序来自数据依赖,当一个函数需要另一个函数的输出作为输入时。
现实世界的副作用在参照上并不透明。您可以将现实世界视为某种有效函数会发生变异的隐式全局状态。由于这种状态,执行顺序很重要:如果您首先从数据库读取然后更新它,反之亦然,这会有所不同。
Haskell 是一种纯函数式语言,它的所有函数都是引用透明的,编译基于此保证。那么,我们如何处理操纵某些全局现实世界状态并需要按特定顺序执行的有效函数呢?通过在这些函数之间引入数据依赖性。
这正是 IO 所做的:在幕后,IO 类型将一个有效的函数与一个虚拟状态参数包装在一起。每个 IO 操作都将此虚拟状态作为输入并将其作为输出提供。将这个虚拟状态参数从一个 IO 操作传递到下一个会创建数据依赖关系,从而告诉 Haskell 编译器如何正确排序所有 IO 操作。
您看不到 dummy state 参数,因为它隐藏在一些语法糖后面:do符号 inmain和其他 IO 操作,以及IO类型内部。