给定Haskell 值(按Rein Heinrich comment编辑f
} f :: IO Int
f = ... -- ignoring its implementation
:
f
使用Idris引用"类型驱动开发,"
纯函数的关键属性是相同的输入总是产生相同的结果。此属性称为引用透明度
是IO ...
,也就是说,Haskell中的所有lookInDatabase :: IO DBThing
函数都是纯粹的?在我看来,从那以后,return MyDbThing
,赢得总是返回相同的值,因为:
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()
的功能纯度。
答案 0 :(得分:6)
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
和另一个世界;它总是被称为不同的世界,这就是为什么即使用相同的方式调用它也可以返回不同的DBThing
值DBThing
)。另一种解释是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 ...
函数)是否纯净?
所以您真正要问的是:
IO
定义是纯吗?您真的不会喜欢它。
深思熟虑。
我们不知道。
从Haskell 2010 report的6.1.7节(第75页)开始:
IO
类型用作与外界交互的操作(动作)的标记。IO
类型是抽象的:用户看不到任何构造函数。IO
是Monad
和Functor
类的实例。
关键点是:
IO
类型为抽象
IO
类型没有“标准定义”,因此无法确定其是否纯净,更不用说该类型的表达式了。我们甚至无法提供简单的证据来证明IO
是单原子的(即,它满足monad laws),因为return
和(>>=)
不能定义 在标准的Haskell 2010中。
要了解有关这如何影响各种与IO
相关的属性的确定,请参阅:
Tackling the Awkward Squad(从第20页的3.2节开始)。
因此,当您下次听到或阅读Haskell是“相对透明”或“纯功能”时,您现在知道(至少对于I / O而言)它们只是猜测-没有 actual 标准定义意味着无法证明或反驳它们。
(如果您现在想知道Haskell如何进入这种状态,我将提供更多详细信息here。)