这是关于Haskell IO的一个合理观点吗?

7
这是Haskell IO的合理解释吗?
当程序运行时,Haskell运行时会执行以下步骤:
1. 调用main函数获取“IO计算”。 2. 然后执行或“运行”该计算,从而执行计算包含的所有副作用。
这种两阶段的方法使得main函数仍然保持纯函数。
在这种情况下,IO计算类似于Haskell的一个特殊版本,具有明确的顺序-或者也许有更好的描述方法?

这确实是一个相当好的解释。但是,在Haskell中,main不是一个函数,只是一个值。(另一方面,在Frege中,它是一个函数,因为它获取传递的命令行参数列表) - Ingo
1
这并不完全有效,因为计算的结果可能会影响未来的计算(例如 getLine)。 - ehird
6
是的,它可以。>>=只是构建IO值的一种方式。当处理异常等复杂情况时会变得更加困难。 - Philip JF
@Porges 是的,但这并不能帮助那个“处理主要事务”的人。他仍然必须知道如何链接操作。 - Owen
@Porges 没错,抱歉我误解了。我只是想说它是一个单子,因为它可以链接,而不是因为它可以链接所以它是一个单子。但我认为我们的意思是一样的。 - Owen
显示剩余3条评论
2个回答

12

是的,那是一个不错的语义模型,用于说明程序的执行方式。 当然,实现并不是像那样工作的,但您仍然可以使用该模型来推理程序。

更一般地说,IO 所做的是允许您将命令式程序视为纯值。 Monad 操作允许您从较小的命令式程序(或在此上下文中使用的常用术语,actions)和纯函数组合命令式程序。 因此,纯函数模型虽然不能执行命令式程序,但仍可将其描述为类型为IO a的表达式,并且编译器可以将这些描述转换为命令式代码。

或者,您可以这样说:

  • 编译器(而不是运行时)评估main
  • 该评估的结果是一个命令式程序。
  • 该程序保存到目标可执行文件中。
  • 然后执行目标程序。

即您所描述的“评估main”部分被推送到编译器中,而不是运行时中。


+1 我喜欢“编译器评估main”的解释,尽管你可以对任何具有编译器的编程语言说出这个观点。但你准确地抓住了重点:Haskell 的不同之处在于,你可以从较小的IO操作中组合出更复杂的IO操作。 - Dan Burton
@PhilipJF 我理解你的立场,但是对于许多人来说,“编译器”或“运行时”仍然具有教学价值。可以将其视为一个垫脚石。或者这样说:通常向新手教授高级主题的最佳方法是“欺骗”他们,然后在他们吸收了“谎言”之后,解释最初的谎言存在的不足之处。在这种情况下,我们希望能够独立于语言实现地推理程序。 - Luis Casillas
2
我对你的观点有一半的认同,并且认为你的回答相当不错。虽然,至少对于没有紧急背景的人来说,我认为基于纯语言的方法可以是教授这些知识的合适方式。事实上,我已经幻想了很长时间,要为只有数学背景的人编写一本关于计算机体系结构的教材。从λ演算、函数式编程和一些代数(范畴、单子等)开始,我们将推导出逻辑门、简单电路、RISC计算机、编译器和基本操作系统。 - Philip JF
2
我并不认同“编译器评估 main 函数”的说法;例如,表达式 if elem 10000000 [1..] then getLine else putStrLn "hello"getLine 得出的结果相同,然而 main = if elem 10000000 [1..] then getLine else putStrLn "hello"main = getLine 可能会得到不同的可执行文件。如果编译器真的评估了 main 函数,那么这就不成立了。 - sdcvvc
作为一名数学家,最近才开始涉足编程领域,如果您能写那本书,我会非常高兴! - Chris Taylor
显示剩余2条评论

6

你对IO的理解非常好,但我对这行代码有问题:

调用 main 函数来获得一个“IO 计算”

最好的方式是把 Haskell 理解为函数并不会执行任何操作。相反,你声明式地描述值是什么。程序由一个称为mainIO值的描述组成。它“调用 main”的唯一意义在于main的声明被简化为弱头正规形式(或类似形式)。

IO是任意带副作用的计算类型。Haskell 的纯子集是纯粹的值描述,它允许不可判定的描述。将 Haskell 视为数学语言,如集合论。集合论中的语句并不会做任何事情,但它们可能涉及诸如“包含 Ackerman_function(30)的最小集合”之类的复杂计算。它们还可以包含不可判定的语句,如“S = 不包含自身的所有集合的集合”

@amindfv 说得部分正确:main不是“纯函数”。它根本不是函数。它是一个值,由纯约简定义,编码了不纯的计算。


我认为海报的目标是为命令式程序员提供更多的摘要。在Haskell中,我们会用“Evaluates”代替“Calls”,但除此之外基本相同。“IO computation”也只是“IO monad”的一种变体。我认为在“真正的Haskellspeak”中,我们会说运行时“评估主函数以获取IO monad中的值”,但这只是纠结。原句对于向命令式程序员解释它很好用。 :) - porges
我认为大多数情况下,“IO计算”比“IO单子”更可取。至于严谨性:发帖者的理解已经很好了,我想深入探讨一下。从我的角度来看,“以Haskell思考”的最重要的事情不是把语言看作“执行”,而是把它看作“存在”。 “调用”携带了太多过程性的负担,并不一定正确:请参见sacundim关于主函数被“编译”而不是“调用”的描述。我认为“评估”坚持了指示而没有引入太多关于如何的内容。这个想法是更深入的理解。 - Philip JF

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接