函数式语言如何模拟副作用?
既然副作用破坏了引用透明性,那么它们是否违背了函数式语言的观点呢?
Since side-effects break referential transparency, don't they go against the point of functional languages?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
纯函数式编程语言使用两种技术来模拟副作用:
1) 表示外部状态的世界类型,其中类型系统保证该类型的每个值仅使用一次。
在使用这种方法的语言中,函数
print
和read
可能具有类型(string, world) ->世界
和世界-> (string, world)
分别。它们可以这样使用:
但不是这样:(
因为 w 使用了两次)
所有具有副作用的内置函数都将获取并返回一个世界值。由于所有具有副作用的函数要么是内置函数,要么调用其他具有副作用的函数,这意味着所有具有副作用的函数都需要获取并返回一个世界。
这样,就不可能使用相同的参数两次调用具有副作用的函数,并且不会违反引用透明度。
2) IO monad,其中所有具有副作用的操作都必须在该 monad 内执行。
通过这种方法,所有具有副作用的操作都将具有类型
io some
。例如print
将是一个类型为string ->; 的函数。 io unit
和read
的类型为io string
。访问执行操作的值的唯一方法是使用“一元绑定”操作(例如,在 haskell 中称为 >>=),将 IO 操作作为一个参数,并使用一个描述如何处理结果的函数,如下所示另一个操作数。
上面的例子对于 Monadic IO 来说是这样的:
There are two techniques that are used by purely functional programming languages to model side effects:
1) A world type that represents external state, where each value of that type is guaranteed by the type system to be used only once.
In a language that uses this approach the function
print
andread
might have the types(string, world) -> world
andworld -> (string, world)
respectively.They might be used like this:
But not like this:
(because w is used twice)
All built-in functions with side-effects would take and return a world value. Since all functions with side-effects are either built-ins or call other functions with side-effects, this means that all functions with side-effects need to take and return a world.
This way it is not possible to call a function with side-effects twice with the same arguments and referential transparency is not violated.
2) An IO monad where all operations with side effects have to be executed inside that monad.
With this approach all operations with side effects would have type
io something
. For exampleprint
would be a function with typestring -> io unit
andread
would have typeio string
.The only way to access the value of performing operation would be to use the "monadic bind" operation (called >>= in haskell for example) with the IO operation as one argument and a function describing what to do with the result as the other operand.
The example from above would look like this with monadic IO:
有多种选项可用于以函数式语言处理 I/O。
函数式编程而不是强制执行。这是迄今为止最常见的问题解决方案
函数式编程中的 I/O。 (示例:Lisp、Scheme、Standard ML、Erlang 等)
想要更多信息。 (提示:您可能不知道。)
I/O 周围的数据充当保持引用完整性所需的“不同值”
活。如果没记错的话,多种 ML 方言都会使用它。
在 Haskell 中使用 monad 来扮演这个角色。请注意,理论上,这与
封面,但是删除了跟踪“世界”/“延续”状态变量的乏味。
有一篇研究论文详尽地分析了这些。
函数式 I/O 是一个持续的研究领域,还有其他语言以有趣且令人费解的方式解决这个问题。 霍尔逻辑在某些研究语言中使用。其他(例如 Mercury)使用 唯一性输入。还有一些(例如 Clean)使用 效果系统。其中我对水星的接触非常非常有限,所以我无法对细节发表评论。有一个 不过,如果您对这个方向感兴趣,请参阅论文,深入详细介绍 Clean 的 I/O 系统。
There are several options available to handle I/O in a functional language.
functional programming rather than enforcing it. This is by far the most common solution to the problem
of I/O in functional programming. (Examples: Lisp, Scheme, Standard ML, Erlang, etc.)
want more information. (Hint: you probably don't.)
of data around with your I/O that acts as the necessary "different value" to keep referential integrity
alive. This is used by several ML dialects if memory serves.
being the use of monads in this role in Haskell. Note that this is, notionally, the same thing under
the covers, but the tedium of keeping track of "world"/"continuation" state variables is removed.
There's a research dissertation that exhaustively analyses these.
Functional I/O is an ongoing field of research and there are other languages which address this issue in interesting and mind-mangling ways. Hoare logic is put to use in some research languages. Others (like Mercury) use uniqueness typing. Still others (like Clean) use effect systems. Of these I have a very, very limited exposure to Mercury only, so I can't really comment on details. There's a paper that details Clean's I/O system in depth, however, if you're interested in that direction.
据我所知,如果你想在函数式语言中产生副作用,你必须显式地对其进行编码。
To the best of my understanding, if you want to have side effects in a functional language, you have to code them explicitly.
这取决于函数式语言:
标准机器学习允许自由使用副作用像大多数过程语言一样,例如 Fortran、Algol、Pascal、C 等。
IO
、ST
和STM
,这有助于保持引用透明度。Clean 也限制了副作用,但这是通过其扩展类型系统实现的。< /p>
Coq 使用的函数式语言 - Gallina -提供根本无法获得副作用。
一种不经常提及的方法依赖于伪数据:在可访问的结构化值(通常是树)中传达单独的一次性抽象值,仅当每个抽象值被最初使用。有关更多信息,请参阅 F. Warren Burton 的 非决定论函数式编程语言中的引用透明度。还可以在 GHC 中找到一个工作示例:其
独特的
名称提供类型。但是,如果使伪数据工作所需的额外参数太烦人,那么实际上可以将 I/O 及其可观察的效果与非严格语义有效地结合起来......如果您并不真正需要引用透明度。
It depends on the functional language:
Standard ML allows the liberal use of side-effects like most procedural languages e.g. Fortran, Algol, Pascal, C, etc.
Haskell restricts side-effects through the use of abstract data types like
IO
,ST
andSTM
, which helps to preserve referential transparency.Clean also restricts side-effects, but does this with its extended type system.
The functional language Coq uses - Gallina - provides no access to side-effects at all.
One approach which isn't regularly mentioned relies on pseudo-data: individual single-use abstract values conveyed in an accessible structured value (commonly a tree), with the side effects only occuring when each abstract value is initially used. For more information, see F. Warren Burton's Nondeterminism with Referential Transparency in Functional Programming Language. An working example can also be found in GHC: its
Unique
name-supply type.But if the extra parameters needed to make pseudo-data work is just too annoying, it is actually possible to usefully combine I/O and its observable effects with non-strict semantics...if you don't really need referential transparency.