我处理函数编程的概念已有一段时间了,发现它很有趣,令人着迷且令人兴奋。特别是纯函数的概念在各个方面都很棒。
但是我没有一件事:将自己限制为纯函数时如何处理副作用。
例如,如果我想计算两个数字的和,则可以编写一个纯函数(在JavaScript中):
var add = function (first, second) {
return first + second;
};
没问题。但是,如果我想将结果打印到控制台怎么办?从定义上讲,“将某些内容打印到控制台”的任务不是纯粹的-但是我应该/应该如何用一种纯函数式编程语言来处理呢?
有几种方法可以做到这一点。您将不得不接受的一件事是,在某个时刻,存在着一种神奇的不纯机器,该机器采用纯表达式并通过与环境交互来使它们不纯。您不应该问关于这个神奇机器的问题。
我可以想到两种方法。我至少已经忘记了第三个人。
最容易理解的方法可能是流I / O。您的main
函数有一个参数:系统上发生的一系列事件–包括按键,文件系统上的文件等等。您的main
函数还返回一件事:您希望在系统上发生的一系列事情。
请注意,流就像列表一样,一次只能创建一个元素,而接收者在构建元素后会立即收到它。您的纯程序从此类流中读取,并在希望系统执行某些操作时将其追加到其自己的流中。
完成所有这些工作的粘合剂是一台神奇的机器,它位于程序之外,从“请求”流中读取内容并将内容放入“答案”流中。虽然您的程序是纯程序,但是这台神奇的机器却不是。
输出流看起来像这样:
[print('Hello, world! What is your name?'), input(), create_file('G:\testfile'), create_file('C:\testfile'), write_file(filehandle, 'John')]
并且相应的输入流将是
['John', IOException('There is no drive G:, could not create file!'), filehandle]
看看插播广告是如何input
导致'John'
出现在插播广告中的?这就是原则。
Haskell所做的就是Monadic I / O,而且确实做得很好。您可以想象这就像用操作员构建一棵巨大的I / O命令树将它们粘合在一起,然后您的main
函数将这个庞大的表达式返回到位于程序外部的神奇机器上,并执行命令并执行指示的操作。这台神奇的机器是不纯洁的,而您的表情构建程序却是纯洁的。
您可能想像一下此命令树看起来像
main
|
+---- Cmd_Print('Hello, world! What is your name?')
+---- Cmd_WriteFile
|
+---- Cmd_Input
|
+---+ return validHandle(IOResult_attempt, IOResult_safe)
+ Cmd_StoreResult Cmd_CreateFile('G:\testfile') IOResult_attempt
+ Cmd_StoreResult Cmd_CreateFile('C:\testfile') IOResult_safe
它要做的第一件事是打印问候语。接下来要做的是它要写入一个文件。为了能够写入文件,它首先需要从输入中读取应该写入文件的任何内容。然后应该有一个要写入的文件句柄。它从一个称为函数的函数获取此函数,该函数validHandle
返回两个替代方案的有效句柄。这样,您可以将看似不纯的代码与看似纯代码的内容混合在一起。
这个“解释”几乎是在问关于您不应该问的神奇机器的问题,因此,我将用一些智慧总结一下。
真正的Monadic I / O在我的示例中看起来很遥远。我的示例是有关单子I / O在不破坏纯度的情况下看起来像“引擎盖下”的可能解释之一。
千万不能尝试使用我的例子来说明如何理解与纯粹的I / O工作。事物在幕后的工作方式与您使用事物做事的方式完全不同。如果您一生中从未见过汽车,那么阅读其中任何一个的蓝图都不会成为一名好司机。
我一直说你不应该问关于真正起作用的神奇机器的问题的原因是,当程序员学习东西时,他们倾向于戳破机器以试图弄清楚它。我不建议您将其用于纯I / O。该机器可能不会教您任何有关如何使用不同I / O变体的知识。
这类似于您通过查看反汇编的JVM字节码而不学习Java的方式。
一定要学习使用monadic I / O和基于流的I / O。这是一次很酷的体验,在您的工具带下拥有更多的工具总是一件好事。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句