在没有副作用的情况下,最常用的模式是在函数式语言中使用数据库吗?

All*_*ate 9 functional-programming side-effects

我试图了解功能语言的核心概念:

"函数式语言的一个核心概念是函数的结果是由它的输入决定的,只有它的输入决定.没有副作用!"

http://www.haskell.org/haskellwiki/Why_Haskell_matters#Functions_and_side-effects_in_functional_languages

我的问题是,如果一个函数只在其本地环境中进行更改,并返回结果,它如何与数据库或文件系统交互?根据定义,不会访问实际上是全局变量或全局状态的东西吗?

用于解决或解决此问题的最常见模式是什么?

dfl*_*str 11

仅仅因为函数式语言是功能性的(也许甚至完全像Haskell一样纯粹!),这并不意味着用该语言编写的程序在运行时必须是纯粹的.

例如,在处理副作用时,Haskell的方法可以简单地解释:让整个程序本身是纯粹的(意味着函数总是为相同的参数返回相同的值,并且没有任何副作用),但是让main函数的返回值成为可以运行的动作.

试图用伪代码来解释这个问题,这里有一些命令式,非功能性的语言:

main:
  read contents of abc.txt into mystring
  write contents of mystring to def.txt
Run Code Online (Sandbox Code Playgroud)

上述main过程就是:描述如何执行一系列操作的一系列步骤.

将其与Haskell等纯功能语言进行比较.在函数式语言中,一切都是表达式,包括主函数.因此可以像这样读取上述程序的等价物:

main = the reading of abc.txt into mystring followed by
       the writing of mystring to def.txt
Run Code Online (Sandbox Code Playgroud)

因此,main是一个表达式,在评估时,将返回描述要执行程序的操作的操作.这个动作的实际执行发生在程序员世界之外.这就是它的工作原理; 以下是可以编译和运行的实际Haskell程序:

main = readFile "abc.txt" >>= \ mystring ->
       writeFile "def.txt" mystring
Run Code Online (Sandbox Code Playgroud)

a >>= b在这种情况下,可以说是"行动a结果所遵循的a行动b",而运营商的结果是联合行动a和b.上述程序当然不是惯用的Haskell; 可以按如下方式重写它(删除多余的变量):

main = readFile "abc.txt" >>=
       writeFile "def.txt"
Run Code Online (Sandbox Code Playgroud)

...或者,使用语法糖和记号:

main = do
  mystring <- readFile "abc.txt"
  writeFile "def.txt" mystring
Run Code Online (Sandbox Code Playgroud)

所有上述程序不仅相同,而且就编译器而言它们是相同的.

这就是如何将文件,数据库系统和Web服务器编写为纯函数式程序:通过程序对操作值进行线程处理以便将它们组合在一起,最后在main函数中结束.这为程序员提供了对程序的巨大控制,这也是纯函数式编程语言在某些情况下如此吸引人的原因.


Mat*_*ick 5

处理函数式语言中副作用和杂质的最常见模式是:

  • 务实,不是纯粹主义者
  • 提供允许不纯的代码和副作用的内置函数
  • 尽可能少地使用它们!

例子:

  • Lisp语言/计划: set!
  • Clojure:refs,并在java对象上使用变异方法
  • Scala:用.创建变量 var
  • ML:不确定具体细节,但维基百科说它允许一些杂质

Haskell做了一点作弊 - 它的解决方案是,对于访问文件系统或数据库的函数,该瞬间整个Universe 的状态,包括文件系统/ db的状态,将被传递给该函数. (1)因此,如果你可以在那一瞬间复制整个宇宙的状态,那么你可以从这样一个函数得到两次相同的结果.当然,你不能在那个瞬间复制整个宇宙的状态,因此函数返回不同的值......

但是Haskell的解决方案,恕我直言,并不是最常见的.


(1)不确定这里的具体细节.感谢CAMcCann指出这个比喻被过度使用,可能并不那么准确.

  • 对于它的价值,这不是Haskell中实际上*工作*的方式,尽管这是一个常用的比喻.所有真正发生的事情是类型系统用于确保不纯函数被标记为这样并以明确定义的顺序执行."通过宇宙的状态"比喻用于实现这一点,但从概念上讲,它在严密的检查中并不能很好地保持. (4认同)

hug*_*omg 5

访问数据库与其他输入输出情况没有什么不同,例如print(17).

在 LISP 和 ML 等急切评估的语言中,有效编程的常用方法就是使用副作用,就像大多数其他编程语言一样。

在 Haskell 中,IO 问题的解决方案是使用 monad。例如,如果您检查HDBC(一个 haskell 数据库库),您可以看到其中有很多返回 IO 操作的函数。

有些语言,例如 Clean,使用唯一性类型来强制 Haskell 对 monad 执行的相同类型的顺序性,但现在这些语言很难找到。