功能语言如何模拟副作用?

时间:2010-10-03 15:31:21

标签: functional-programming io referential-transparency

由于副作用破坏了引用透明度,它们是否违背了函数式语言的要点?

3 个答案:

答案 0 :(得分:22)

纯函数式编程语言使用两种技术来模拟副作用:

1)表示外部状态的世界类型,其中该类型的每个值都由类型系统保证仅使用一次。

在使用此方法的语言中,函数printread可能分别具有(string, world) -> worldworld -> (string, world)类型。

他们可能会像这样使用:

let main w =
  let w1 = print ("What's your name?", w) in
  let (str, w2) = read w1 in
  let w3 = print ("Your name is " ^ name, w2) in
  w3

但不是这样的:

let main w =
  let w1 = print ("What's your name?", w) in
  let (str, w2) = read w in
  let w3 = print ("Your name is " ^ name, w2) in
  w3

(因为w使用了两次)

所有带副作用的内置函数都会获取并返回一个世界值。由于所有带副作用的函数都是内置函数或调用其他具有副作用的函数,这意味着所有带副作用的函数都需要获取并返回一个世界。

这样就不可能使用相同的参数调用带有副作用的函数两次,并且不会违反参照透明度。

2)IO monad,其中所有带副作用的操作都必须在该monad中执行。

使用此方法,所有带副作用的操作都将具有类型io something。例如,print是一个类型为string -> io unit的函数,而read的类型为io string

访问执行操作值的唯一方法是使用“monadic bind”操作(例如,在haskell中称为>> =),IO操作作为一个参数,以及描述如何处理的函数结果作为另一个操作数。

上面的例子看起来像是monadic IO:

let main =
  (print "What's your name?") >>=
  (lambda () -> read >>=
  (lambda name -> print ("Your name is " ^ name)))

答案 1 :(得分:12)

有多种选项可用于处理函数式语言的I / O.

  • 不要纯洁。许多功能语言不是纯粹的功能。更多的是他们支持 函数式编程而不是强制执行。这是迄今为止最常见的问题解决方案 函数式编程中的I / O. (示例:Lisp,Scheme,Standard ML,Erlang等)
  • 流转换。早期的Haskell I / O就是这样完成的。如果您有详细信息,请查看下面的链接 想了解更多信息。 (提示:你可能没有。)
  • 继续传递I / O(其他答案中提到的“世界传递”)。在这一个你传递一个令牌 您的I / O周围的数据,作为保持参照完整性的必要“不同值” 活。如果内存服务,则由几种ML方言使用。
  • 上面的“延续”或“世界”事物可以包含在各种数据类型中,最常见的是(in) 在Haskell中使用monad这个角色。请注意,从概念上讲,这是相同的事情 封面,但删除了跟踪“世界”/“继续”状态变量的乏味。

a research dissertation详尽地分析了这些。

功能I / O是一个正在进行的研究领域,还有其他语言以有趣和精神错乱的方式解决了这个问题。 Hoare logic用于某些研究语言。其他人(如Mercury)使用uniqueness typing。还有一些人(如Clean)使用effect systems。其中我对水星的接触非常非常有限,所以我无法对细节做出真正的评论。有一个paper详细介绍了Clean的I / O系统,但是,如果你对这个方向感兴趣的话。

答案 2 :(得分:1)

据我所知,如果你想在功能语言中有副作用,你必须明确地对它们进行编码。