如何在纯函数式编程中执行副作用? [英] How to perform side-effects in pure functional programming?

查看:113
本文介绍了如何在纯函数式编程中执行副作用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我现在正在处理函数式编程的概念,并发现它非常有趣,令人着迷和令人兴奋。特别是纯功能的概念非常棒,用各种各样的方式。



但有一点我不明白:如何处理边 - 如果我想计算两个数字的总和,我可以编写一个纯函数(使用JavaScript):



p>

  var add = function(first,second){
return first + second;
};

完全没问题。但是如果我想将结果打印到控制台呢? 将东西打印到控制台的任务不是纯粹的定义 - 但是我怎么能/我该怎么处理这个纯粹的函数式编程语言呢? 解决方案

这有几种方法。你不得不接受的一件事是,在某些时候,存在一个神奇的不纯的机器,它会接受纯粹的表达,并通过与环境交互使它们变得不纯洁。你不应该问关于这个神奇的机器的问题。



有两种方法我可以想到我的头顶。至少有第三个我已经忘记了。




I / O流



最容易理解的方法可能是流式I / O。您的 main 函数有一个参数:系统上发生的一系列事情 - 包括按键,文件系统上的文件等等。您的 main 函数也会返回一件事情:您希望在系统上发生的事情。



Streams就像列表一样,只要你可以一次构建一个元素,收件人一旦建立它就会收到元素。您的纯粹程序会从这样的流中读取,并在系统执行某些操作时附加到它自己的流中。



使所有这些工作成为可能的胶水是一个神奇的位于程序之外的机器,从请求流中读取数据并将其放入答案流中。虽然你的程序是纯粹的,但这个神奇的机器不是。



输出流可能如下所示:

  [print('Hello,world!What is your name?'),input(),create_file('G:\testfile'),create_file('C:\testfile') ,write_file(filehandle,'John')] 

和相应的输入流将是

  ['John',IOException('没有驱动器G :,无法创建文件!',文件句柄] 

查看输出中的输入如何导致'John'出现在插播视频中?这就是原则。

Monadic I / O



Monadic I / O是Haskell所做的,好。你可以想象这是构建一个巨大的I / O命令树,操作符将它们​​粘合在一起,然后你的 main 函数将这个巨大的表达式返回给位于外部的神奇机器的程序并执行这些命令并执行指示的操作。这个神奇的机器是不纯的,而你的表达建立程序是纯粹的。



你可能想要这个命令树看起来像

  main 
|
+ ---- Cmd_Print('Hello,world!你叫什么名字?')
+ ---- 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 的函数中得到这个函数,它返回两个选择的有效句柄。通过这种方式,您可以将不纯代码与看起来像纯代码的代码混合在一起。






这个解释在提问有关这款神奇机器的问题时,你不应该提出问题,所以我会用几件智慧来包装它。




  • 真正的单点I / O在我的例子看起来远不及这里。我的例子是单程I / O如何在不破坏纯度的情况下看起来像引擎盖下的可能解释之一。


  • Do not 尝试使用我的示例来了解如何使用纯I / O。引擎盖下的东西是如何完全不同于你如何处理它。如果你以前从未见过一辆汽车,那么你也不会因为阅读蓝图而成为一名好车手。



    我之所以一直说你'不应该问关于实际做什么的神奇机器的问题是,当程序员学习东西的时候,他们倾向于嘲弄机器来试图找出它。我不建议这样做纯I / O。机器可能没有教你任何有关如何使用不同的I / O变体的知识。

    字节码。
  • 学习使用单点I / O和基于流的I / O。这是一个很酷的体验,在你的工具带下有更多的工具总是很好的。



I am dealing with the concept of functional programming for a while now and find it quite interesting, fascinating and exciting. Especially the idea of pure functions is awesome, in various terms.

But there is one thing I do not get: How to deal with side-effects when restricting yourself to pure functions.

E.g., if I want to calculate the sum of two numbers, I can write a pure function (in JavaScript):

var add = function (first, second) {
  return first + second;
};

No problem at all. But what if I want to print the result to the console? The task of "printing something to the console" is not pure by definition - but how could / should I deal with this in a pure functional programming language?

解决方案

There are a few approaches to this. One thing you will just have to accept is that at some point, there exists a magical impure machine that takes pure expressions and makes them impure by interacting with the environment. You are not supposed to ask questions about this magical machine.

There are two approaches I can think of off the top of my head. There exists at least a third one I have forgotten about.


I/O Streams

The approach that is easiest to understand could be streaming I/O. Your main function takes one argument: a stream of things that have happened on the system – this includes keypresses, files on the file system, and so on. Your main function also returns one thing: a stream of things that you want to happen on the system.

Streams are like lists, mind you, only you can build them one element at a time and the recipient will receive the element as soon as you have built it. Your pure program reads from such a stream, and appends to its own stream when it wants the system to do something.

The glue that makes all of this work is a magical machine that sits outside of your program, reads from the "request" stream and puts stuff into the "answers" stream. While your program is pure, this magical machine is not.

The output stream could look like this:

[print('Hello, world! What is your name?'), input(), create_file('G:\testfile'), create_file('C:\testfile'), write_file(filehandle, 'John')]

and the corresponding input stream would be

['John', IOException('There is no drive G:, could not create file!'), filehandle]

See how the input in the out-stream resulted in 'John' appearing in the in-stream? That's the principle.

Monadic I/O

Monadic I/O is what Haskell does, and does really well. You can imagine this as building a giant tree of I/O commands with operators to glue them together, and then your main function returns this massive expression to a magical machine that sits outside of your program and executes the commands and performs the operations indicated. This magical machine is impure, while your expression-building program is pure.

You might want to imagine this command tree looking something like

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

The first thing it does is print a greeting. The next thing it does is that it wants to write a file. To be able to write to the file, it first needs to read from the input whatever it's supposed to write to the file. Then it is supposed to have a file handle to write to. It gets this from a function called validHandle that returns the valid handle of two alternatives. This way, you can mix what looks like impure code with what looks like pure code.


This "explanation" is bordering on asking questions about the magical machine you're not supposed to ask questions about, so I'm going to wrap this up with a few pieces of wisdom.

  • Real monadic I/O looks nowhere near my example here. My example is one of the possible explanations for how monadic I/O can look like "under the hood" without breaking purity.

  • Do not try to use my examples to understand how to work with pure I/O. How something works under the hood is something completely different to how you do things with it. If you had never seen a car before in your life, you wouldn't become a good driver by reading the blueprints for one either.

    The reason I keep saying you're not supposed to ask questions about the magical machine that actually does stuff is that when programmers learn things, they tend to want to go poke at the machinery to try to figure it out. I don't recommend doing so for pure I/O. The machinery might not teach you anything about how to use different variants of I/O.

    This is similar to how you don't learn Java by looking at the disassembled JVM bytecode.

  • Do learn to use monadic I/O and stream-based I/O. It's a cool experience and it's always good to have more tools under your toolbelt.

这篇关于如何在纯函数式编程中执行副作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆