通过IO了解Haskell中的纯函数

时间:2015-12-15 03:39:13

标签: haskell

给定Haskell (按Rein Heinrich comment编辑f} f :: IO Int f = ... -- ignoring its implementation

f

使用Idris引用"类型驱动开发,"

  

纯函数的关键属性是相同的输入总是产生相同的结果。此属性称为引用透明度

IO ...,也就是说,Haskell中的所有lookInDatabase :: IO DBThing函数都是纯粹的?在我看来,从那以后,return MyDbThing,赢得总是返回相同的值,因为:

  • 在t = 0时,数据库可能已关闭
  • 在t = 1时,数据库可能已启动,f将导致

简而言之,IO ...(和f函数一般)是纯粹的吗?如果是,那么请更正我的错误理解,因为我试图通过我的t=...示例来反驳class Resource(ndb.Model): name = ndb.StringProperty() availability = ndb.StructuredProperty(Availability, repeated=True) tags = ndb.StringProperty(repeated=True) owner = ndb.StringProperty() id = ndb.StringProperty(indexed=True, required=True) lastReservedTime = ndb.DateTimeProperty(auto_now_add=False) startString = ndb.StringProperty() endString = ndb.StringProperty() 的功能纯度。

5 个答案:

答案 0 :(得分:6)

从概念上讲,IO实际上是一种单独的语言。它是Haskell RTS(运行时系统)的语言。它在Haskell中实现为(相对简单的)嵌入式DSL,其“脚本”具有类型IO a

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

从语言设计的角度来看,IO是一个非常优雅的黑客,可以将非纯粹的丑陋完全隔离开,同时将其紧密地融入其纯净的环境中,而不需要使用特殊的外壳。换句话说,该设计并不能解决由不纯的IO引起的问题,但它至少不会影响代码的纯部分。

下一步是研究FRP - 使用FRP,您可以使包含IO的层更薄,并将更多非纯逻辑移动到纯逻辑中。

你可能还想阅读John Backus关于函数编程主题的着作,冯诺依曼架构的局限性等。如果你对纯度和IO之间的关系感兴趣,Conal Elliott也是google的名字。

P.S。另外值得注意的是,虽然IO严重依赖monad来解决懒惰评估的问题,并且因为monad是构建嵌入式DSL的一种非常好的方式(其中IO只是一个例子),monad比IO,所以尽量不要在相同的上下文中考虑IO和monad - 它们是两个独立的东西,两者都可以存在而没有另一个。

答案 1 :(得分:2)

首先,您正确地注意到I / O操作不是纯粹的。这是不可能的。但是,所有功能的纯度都是Haskell的一个有希望的点,所以发生了什么?

无论你喜欢与否,一个适用于a的函数(也可能错误地说"返回")IO Something带有一些参数总是返回具有相同参数的IO Something相同。 IO monad允许你隐藏" monad行为的容器内部的动作。当你有一个IO String时,该函数/对象包含String / [Char],而是一个你将获得的承诺以某种方式String将来。因此,IO包含在需要执行不纯I / O操作时要执行的操作的信息。

毕竟,执行IO操作的唯一方法是使其名称为main,或者是main的依赖关系。由于monad的灵活性,你可以"连接" IO行动。像这样的程序...(注意:这段代码不是一个好主意)

main = do
    input <- getLine
    putStrLn input

是合成糖...

main =
    getLine >>= (\input -> putStrLn input)

这表明main是从标准输出打印到标准输入读取的字符串,后跟换行字符所导致的I / O操作。你看到了魔法吗? IO只是一个包装器,表示在不纯的上下文中的内容,以产生一些给定的输出,而不是该操作的结果,因为那样需要Haskell语言才能接受不纯的代码。

把它想象成一种享受。如果您有一个蛋糕(请阅读:IO monad)来获取蛋糕(请参阅Something中的IO Something),您知道如何制作蛋糕,但是您无法制作蛋糕(因为你可以搞砸那个杰作)。相反,主要负责人(阅读:Haskell运行时系统的最基本部分,负责应用main)为你做了肮脏的工作(阅读:做不纯/非法的东西),最重要的是,他不会犯任何错误(阅读:打破代码纯度)......除非烤箱当​​然坏了(读:System.IO.Error),但他知道如何清理它(阅读:代码将总是保持纯洁。)

这是IO是不透明类型的原因之一。它的实现有点争议(直到你阅读GHC的源代码),最好留下实现定义。

快乐,因为你被纯洁所照亮。许多程序员甚至都不知道Haskell的存在!

我希望这能为你带来一些启示!

答案 2 :(得分:2)

简短回答:是的,f是参考透明的。

无论何时看,它都等于相同的值 但这并不意味着它总是绑定相同的值。

答案 3 :(得分:1)

Haskell正在这里耍手段。 IO既纯粹又不纯净,具体取决于你如何看待它。

关于&#34; IO是纯粹的&#34;一方面,你已经陷入了一个非常常见的错误,即错过了一个返回IO DBThing的函数返回DBThing。当有人声称类型为Stuff -> IO DBThing的函数是纯粹的时,他们表示您可以将其提供给相同的Stuff并始终获得相同的DBThing;正确地指出这是不可能的,也不是很有用!他们正在节省的是,特定Stuff,您总会得到相同的IO DBThing

你实际上根本无法从DBThing获得IO DBThing,因此Haskell不必担心数据库包含不同的值(或不可用)在不同的时间。使用IO DBThing所能做的就是将它与需要DBThing的其他东西结合起来并生成其他类型的IO thing;这种组合的结果是IO thing

Haskell在这里所做的是建立纯Haskell值的操纵与程序外的世界中发生的变化之间的对应关系。你可以用一些普通的纯值来做一些事情,这些值对于改变数据库状态之类的不纯操作没有任何意义。因此,使用IO值与外部世界之间的对应关系,Haskell根本无法为您提供{em> 对应于#&#em}对应的内容的任何操作#&} 39;在现实世界中有意义。

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

但实际上这些都只是隐喻。关键是IO DBThing值只是一个非常有限的界面,它不允许你做任何与真实世界行动无关的事情。

请注意, monad 的概念实际上并未实现。 Haskell的IO系统确实不依赖于monad; IO只是一个方便的界面,如果您只使用通用的monad界面,那么可以打破IO限制(即使你没有& #39;知道你的monad实际上是IO)。由于Monad接口也足够有趣,可以编写很多有用的程序,Monad形成monad的事实允许大量对其他类型有用的代码一般被重用IO

这是否意味着您实际上可以编写纯IO代码?并不是的。这是&#34;当然,IO不是纯粹的&#34;硬币的一面。当您使用花哨的&#34;将IO功能组合在一起时#34;你仍然需要考虑你的程序一个接一个地(或并行地)执行步骤,影响和受外部条件和系统的影响;简而言之,您必须使用命令式语言编写IO代码(仅使用比大多数类型更好的类型系统)。使IO纯粹并不能真正帮助你消除你必须考虑代码的方式中的杂质。

那么重点是什么?好吧,它为我们提供了一个编译器强制执行的代码划分,可以执行IO和无法实现的代码。如果该类型上没有IO标记,则不会涉及不纯的IO。 那个在任何语言中都是有用的。编译器也知道这一点; Haskell编译器可以对非IO代码应用优化,这些代码在大多数其他语言中都是无效的,因为通常不可能知道代码的某一部分没有具有副作用(除非您能够看到代码所调用的所有内容的完整实现,否则可以传递。

另外,由于IO是纯粹的,代码分析工具(包括你的大脑)不必特别处理IO代码。如果您可以选择在与IO代码具有相同结构的纯代码上有效的代码转换,则可以在IO代码上执行此操作。编译器使用它。许多转换被IO代码必须使用的结构排除(为了保持在与外部世界的事物有明显对应的事物的范围内),但它们也将被排除在外任何使用相同结构的纯代码;仔细构建IO接口会使执行顺序依赖&#34;看起来像普通的&#34;数据依赖&#34;,所以你可以使用数据依赖规则来确定使用IO的规则。

答案 4 :(得分:0)

简而言之,f(通常是IO ...函数)是否纯净?

所以您真正要问的是:

Haskell中的IO定义是纯吗?

您真的不会喜欢它。

深思熟虑。

我们不知道

Haskell 2010 report的6.1.7节(第75页)开始:

IO类型用作与外界交互的操作(动作)的标记。 IO类型是抽象的:用户看不到任何构造函数。 IOMonadFunctor类的实例。

关键点是:

IO类型为抽象

IO类型没有“标准定义”,因此无法确定其是否纯净,更不用说该类型的表达式了。我们甚至无法提供简单的证据来证明IO是单原子的(即,它满足monad laws),因为return(>>=)不能定义 在标准的Haskell 2010中。

要了解有关这如何影响各种与IO相关的属性的确定,请参阅:

因此,当您下次听到或阅读Haskell是“相对透明”或“纯功能”时,您现在知道(至少对于I / O而言)它们只是猜测-没有 actual 标准定义意味着无法证明或反驳它们。

(如果您现在想知道Haskell如何进入这种状态,我将提供更多详细信息here。)