副作用是好事吗?

发布于 2024-07-17 12:09:36 字数 1431 浏览 11 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

傲娇萝莉攻 2024-07-24 12:09:37

没有副作用,就无法执行I/O操作; 所以你无法做出有用的应用程序。

Without side effects, you can't perform I/O operations; so you can't make a useful application.

归属感 2024-07-24 12:09:37

这句话真的让我笑了。 也就是说,我发现副作用最小化可以真正转化为更容易推理和维护的代码。 然而,我没有足够的时间去探索函数式编程,正如我所希望的那样。

在使用围绕副作用的面向对象和过程语言时,我看待它的方式是包含隔离副作用。

作为一个基本示例,视频游戏具有将图形渲染到屏幕上的必要副作用。 然而,关于副作用,这里有两种不同的设计路径。

人们试图通过使渲染器变得非常抽象并基本上告诉要渲染什么来最小化和放松耦合。 然后,系统的其他部分告诉渲染器要绘制什么,这可能是一批基元,例如带有投影和模型视图矩阵的三角形和点,或者可能是更高级别的东西,例如抽象模型、相机、灯光和粒子。 不管怎样,这样的设计围绕着许多导致外部副作用的事情,因为代码库的许多部分可能会将更改推送到渲染器(无论多么抽象或间接,最终效果仍然是这样一个整体中的一大堆事情)系统触发外部渲染副作用)。

另一种方法是遏制/隔离这些副作用。 渲染器不是被告知要渲染什么,而是与游戏世界耦合(尽管这可能只是一些基本的抽象,并且可能访问场景图)。 现在,它可以自行访问场景(只读访问),并查看场景并找出要使用更多拉式设计渲染的内容。 这导致渲染器与游戏世界的耦合程度更高,但这也意味着与屏幕输出相关的副作用现在完全包含在渲染器内。

后一种设计包含隔离副作用,而且我发现这种类型的设计更容易维护和保持正确。 它仍然会产生副作用,但与将图形输出到屏幕相关的所有副作用现在完全包含在渲染器中。 如果那里出现问题,您知道错误将出现在渲染器代码中,而不是外部滥用它并告诉它做错误的事情的结果。

正因为如此,当谈到耦合时,我总是发现更理想的是在导致外部副作用的事物中最大化传出(传出)耦合并最小化传入(传入)耦合。 无论抽象如何,这都适用。 在副作用的上下文中,只要就将要发生的副作用进行通信,对 IRenderer 的依赖仍然是对具体 Renderer 的依赖。 就将发生什么副作用而言,抽象并没有什么区别。

渲染器应该依赖于世界的其他部分,以便它可以将这些副作用完全隔离到屏幕上; 世界其他地方不应该依赖渲染器。 文件保护程序也是如此。 外界不应该告诉文件保存程序要保存什么。 它应该审视周围的世界,并自行找出要拯救的内容。 这就是寻求隔离和遏制副作用的设计路径; 它往往更多地是基于拉的而不是基于推的。 如果您绘制出依赖关系,结果往往会引入更多的耦合(尽管可能是松散的),因为保存程序可能需要与它甚至不感兴趣保存的事物耦合,或者渲染器可能需要对事物的只读访问权限它甚至对渲染不感兴趣来发现它对渲染感兴趣的东西。

然而,最终结果是依赖关系远离副作用,而不是流向副作用。 当我们有一个具有许多依赖项来推动外部副作用的系统时,我总是发现这些是最难推理的,因为系统的许多部分可能会改变外部状态,以至于不仅很难弄清楚是什么将会发生,但也有何时何处。 因此,纠正/防止该问题的最直接方法是寻求使依赖项远离副作用,而不是流向副作用。

不管怎样,我发现支持这些类型的设计是一种实用的方法,可以帮助避免错误,也可以帮助检测和隔离它们(当它们存在时),使它们更容易重现和纠正。

我发现的另一个有用的策略是使系统的任何给定循环/阶段的副作用更加均匀。 例如,我发现,在这种情况下,如果您执行三个同构循环,而不是执行一个从某些内容中删除关联数据、取消链接,然后删除它的循环,会容易得多。 第一个同构循环可以删除关联的数据。 第二个同构环路可以使节点脱链。 第三个同质循环可以将其从系统的其余部分中删除。 这是在较低级别的注释上,与实现相关而不是与设计相关,但我经常发现结果更容易推理、维护,甚至优化(更容易并行化,例如,并且具有改进的引用局部性)——你采取这些非同质循环触发多种不同类型的副作用,并将它们分解为多个同质循环,每个循环仅触发一种统一的副作用。

That quote really made me chuckle. That said, I find the minimization of side effects to really translate to code that is much easier to reason about and maintain. However, I don't have the luxury of exploring functional programming quite as much as I would like.

The way I look at it when working in object-oriented and procedural languages that revolve around side effects is to contain and isolate side effects.

As a basic example, a video game has a necessary side effect of rendering graphics to a screen. However, there are two different kinds of design paths here with respect to side effects.

One seeks to minimize and loosen coupling by making the renderer very abstract and basically told what to render. The other parts of the system then tell the renderer what to draw and that could be a batch of primitives like triangles and points with projection and modelview matrices or maybe something higher-level like abstract models and cameras and lights and particles. Either way, such a design revolves around many things causing external side effects, since potentially many parts of the codebase will be pushing changes to the renderer (no matter how abstract or indirect, the net effect is still a whole bunch of things in such a system triggering external rendering side effects).

The other way is to contain/isolate those side effects. Instead of the renderer being told what to render, it instead becomes coupled to the game world (though this could just be some basic abstractions and maybe access to a scene graph). Now it accesses the scene on its own (read-only access) and looks through the scene and figures out what to render using more of a pull-style design. That leads to more coupling from renderer to game world but it also means the side effects related to screen output are now completely contained inside the renderer.

This latter design contains or isolates side effects, and I find that type of design much easier to maintain and keep correct. It still causes side effects but all the side effects related to outputting graphics to a screen are now entirely contained in the renderer. If there's an issue there, you know the bug is going to be in the renderer code and not the result of something external misusing it and telling it the wrong things to do.

Because of this, when it comes to coupling, I have always found it more desirable to maximize efferent (outgoing) couplings in things that cause external side side effects and minimize afferent (incoming) couplings. This applies regardless of abstractions. In the context of side effects, a dependency to IRenderer is still a dependency to a concrete Renderer as far as communication goes with respect to what side effects are going to happen. The abstraction makes no difference as far as what side effects are going to occur.

The renderer should depend on the rest of the world so that it can completely isolate those side effects to the screen; the rest of the world shouldn't depend on the renderer. Same kind of analogy for a file saver. The file saver shouldn't be told what to save by the outside world. It should look at the world around it and figure out what to save on its own. Such would be the design path that seeks to isolate and contain side effects; it tends to be more pull-based than push-based. The result tends to introduce a bit more coupling (though it could be loose) if you graph out the dependencies since the saver might need to be coupled with things it's not even interested in saving, or the renderer might need read-only access to things it's not even interested in rendering to discover the things it is interested in renderering.

However, the end result is that the dependencies flow away from side effects instead of towards side effects. When we have a system with many dependencies flowing towards pushing external side effects, I have always found those the hardest to reason about since so many parts of the system could potentially be changing external states to the point where it's not just hard to figure out what's going to happen but also when and where. So the most straightforward way to correct/prevent that problem is to seek to make dependencies flow away from side effects, not towards them.

Anyway, I have found favoring these types of designs the practical way to help avoid bugs and also help detect and isolate them when they exist to make them easier to reproduce and correct.

Another useful strategy I find is to make side effects more homogeneous for any given loop/phase of the system. For example, instead of doing a loop which removes associated data from something, delinks it, and then removes it, I have found it much easier if you do three homogeneous loops in such cases. The first homogeneous loop can remove associated data. The second homogeneous loop can delink the node. The third homogeneous loop can remove it from the rest of the system. That's on a lower-level note related more to implementation than design, but I have often found the result easier to reason about, maintain, and also even optimize (easier to parallelize, e.g., and with improved locality of reference) -- you take those non-homogeneous loops triggering multiple different types of side effects and break them down into multiple homogeneous loops, each triggering just one uniform kind of side effect.

吲‖鸣 2024-07-24 12:09:37

由于您的程序必须具有副作用才能产生任何输出或有趣的效果(除了加热 CPU 之外),因此问题在于应在程序中的何处触发这些副作用。 只有当它们以您意想不到的方式隐藏时,它们才会变得有害。

根据经验,将纯方法和具有副作用的方法分开。 将某些内容打印到控制台的方法应该只执行此操作,而不是计算您可能想在其他地方使用的一些有趣的值。

Since your program has to have side effects to have any output or interesting effect (apart from heating your CPU), the question is rather where these side effects should be triggered in your program. They become only harmful if they are hidden in methods you don't expect them.

As a rule of thumb: Separate pure methods and methods with side effects. A method that prints something to the console should do only that and not compute some interesting value that you might want to use somewhere else.

嗫嚅 2024-07-24 12:09:37

嗯,一方面,使用副作用进行编程要容易得多、直观得多。 对于很多人来说,函数式编程很难理解——找到一个在 Ocaml 中教授/助教过的人,你可能会听到各种各样关于人们完全无法理解它的故事。 如果没有人能够真正遵循它,那么设计精美、无副作用的功能代码有什么好处呢? 雇用人员来完成您的软件变得相当困难。

至少这是争论的一方面。 有很多原因让很多人不得不学习函数式风格、无副作用的代码。 我想到了多线程。

Well, it's a lot easier and more intuitive to program with side effects, for one thing. Functional programming is difficult for a lot of people to wrap their head around -- find someone who's taught/TAed a class in Ocaml and you'll probably get all kinds of stories about people's abject failure to comprehend it. And what good is having beautifully designed, wonderfully side effect free functional code if nobody can actually follow it? Makes hiring people to get your software done rather difficult.

That's one side of the argument, at least. There's any number of reasons lots of people are going to have to learn all about functional style, side-effect-less code. Multithreading comes to mind.

一笔一画续写前缘 2024-07-24 12:09:37

我认为平衡的答案是,人们应该寻找机会最小化或避免副作用,或者考虑它们在哪里,并寻找机会将它们移到其他地方以使代码更容易理解。

这里我给出了一些代码的两个版本。 当我开始寻找机会将看起来像第一个版本的代码更改为看起来像第二个版本的代码时,我的生活变得更好:

它可能有一个具有属性 first, secondary,third 和方法 first_method、second_method、third_method 类似

def first_method(self):
    # calculate and set self.first

def second_method(self):
    # use self.first to calculate and set self.second

def third_method(self):
    # use self.first and self.second to calculate self.third

,然后是更高级别的方法:

def method(self):
    self.first_method()
    self.second_method()
    self.third_method()

现在想象这些名称不以“first”、“second”、“third”为前缀,而是描述这些方法的作用,并且程序比较复杂。 这是我在探索滥用副作用的代码时经常遇到的问题。 我必须不断地查看函数的实现,以了解调用它们的效果以及它们如何协同工作。

现在,不用对“副作用是邪恶的”东西发疯,我们仍然可以从避免副作用的愿望中受益:

def first_func():
    # calculate the first thing and return it

def second_func(first_thing):
    # use first_thing to calculate second_thing and return it

def third_func(first_thing, second_thing):
    # use first_thing and second_thing to calculate third_thing and return it

并且更高级别的方法可能看起来像

def method(self):
    self.first_thing = first_func()
    self.second_thing = second_func(self.first_thing)
    self.third_thing = third_func(self.first_thing, self.second_thing)

我们仍然有副作用,方法设置属性但是当我们尝试了解组成它的函数如何协同工作时,很明显您必须按此顺序调用它们,并且每个函数需要什么来完成其工作也很清楚。

另外,在第一个版本中,当查看方法的实现时,谁知道每个函数会更改对象的其他哪些属性。 当查看第二个版本时,所有人都可以清楚地看到调用 method 仅更改了三个属性,而无需查看函数的实现。

这个例子可能看起来很简单,因为我为了这个解释而凭空制作了它。 我尽力传达了一些我在尝试理解编写的代码时遇到的实际问题,这些代码对副作用没有一丝蔑视。 像第一个版本一样操作的现实世界代码比像第二个版本一样操作的现实世界代码更难理解。

正如其他人所说,我对副作用的看法是,值得问自己副作用是否可以移动或避免,这样做通常会导致代码更容易理解。

I think the balanced answer is that one should look for opportunities to minimize or avoid side effects or to think of where they are and look for opportunities to move them somewhere else to make code easier to understand.

Here I give two versions of some code. When I started to look for opportunities to change code that looks like the first version into code that looks like the second version, my life got better:

It might have an object with attributes first, second, third and methods first_method, second_method, third_method like

def first_method(self):
    # calculate and set self.first

def second_method(self):
    # use self.first to calculate and set self.second

def third_method(self):
    # use self.first and self.second to calculate self.third

and then a higher level method:

def method(self):
    self.first_method()
    self.second_method()
    self.third_method()

Now imagine the names aren't prefixed by "first", "second", "third" but describe what the methods do, and that the program is more complex. This is a problem I routinely face when exploring code that abuses side effects. I constantly have to look at the implementation of the functions to understand what the effect of calling them is and how they work together.

Now without going nuts with the "side effects are evil" stuff, we can still benefit from a desire to avoid side effects:

def first_func():
    # calculate the first thing and return it

def second_func(first_thing):
    # use first_thing to calculate second_thing and return it

def third_func(first_thing, second_thing):
    # use first_thing and second_thing to calculate third_thing and return it

and that higher level method could look like

def method(self):
    self.first_thing = first_func()
    self.second_thing = second_func(self.first_thing)
    self.third_thing = third_func(self.first_thing, self.second_thing)

We still have side effects, method sets attributes of the object, but when we go to try to understand how the functions that make it up work together, it's crystal clear that you have to call them in this order and it is also crystal clear what each of them need to do their work.

Also, with the first version, when looking at the implementation of method, who knows what other attributes of the object get changed by each function. When looking at the second version, it's plain for all to see that calling method changes only three attributes without having to look at the implementation of the functions.

This example might seem simplistic because I crafted it out of thin air for this explanation. I tried my best to convey some real problems that I have when trying to understand code that was written without a certain slight disdain for side effects. Real world code that operates like the first version is harder to understand than real world code that operates like the second version.

My take on side effects, as others have said, is that it is worth while to ask yourself whether the side effects can be moved or avoided and that doing so often results in code that is easier to understand.

眼泪都笑了 2024-07-24 12:09:36

我经常看到一个关于 SO 的问题,这迫使我花半个小时编辑一篇非常糟糕的维基百科文章。 这篇文章现在只是中等程度的糟糕。 在与你的问题有关的部分,我写道:

在计算机科学中,如果函数或表达式除了产生值之外,还修改某些状态或具有可观察的值,则称该函数或表达式具有副作用与调用函数或外部世界的交互。 例如,函数可能会修改全局或静态变量、修改其参数之一、引发异常、将数据写入显示器或文件、读取数据、调用其他副作用函数或发射导弹。 在存在副作用的情况下,程序的行为取决于过去的历史记录; 也就是说,评估的顺序很重要。 因为理解一个有效的程序需要考虑所有可能的历史,所以副作用通常会使程序更难理解。

副作用对于程序与外界(人、文件系统、网络上的其他计算机)交互至关重要。 但副作用的使用程度取决于编程范例。 命令式编程因不受控制、滥用副作用而闻名。 在函数式编程中,很少使用副作用。 Standard ML 和Scheme 等函数式语言并不限制副作用,但程序员通常会避免副作用。 函数式语言 Haskell 通过静态类型系统来限制副作用; 只有产生 IO 类型结果的函数才会有副作用。

Every so often I see a question on SO which forces me to spend half an hour editing a really bad Wikipedia article. The article is now only moderately bad. In the part that bears on your question, I wrote as follows:

In computer science, a function or expression is said to have a side effect if, in addition to producing a value, it also modifies some state or has an observable interaction with calling functions or the outside world. For example, a function might modify a global or a static variable, modify one of its arguments, raise an exception, write data to a display or file, read data, call other side-effecting functions, or launch missiles. In the presence of side effects, a program's behavior depends on past history; that is, the order of evaluation matters. Because understanding an effectful program requires thinking about all possible histories, side effects often make a program harder to understand.

Side effects are essential to enable a program to interact with the outside world (people, filesystems, other computers on networks). But the degree to which side effects are used depends on the programming paradigm. Imperative programming is known for uncontrolled, promiscuous use of side effects. In functional programming, side effects are rarely used. Functional languages such as Standard ML and Scheme do not restrict side effects, but it is customary for programmers to avoid them. The functional language Haskell restricts side effects with a static type system; only a function that produces a result of IO type can have side effects.

甜嗑 2024-07-24 12:09:36

副作用是一种不可避免的罪恶,人们应该设法将其最小化/局部化。

该线程上的其他评论说,无效果编程有时并不那么直观,但我认为人们所认为的“直观”很大程度上是他们先前经验的结果,并且大多数人的经验都具有很强的必要性偏见。 主流工具每天都变得越来越实用,因为人们发现,由于单独组件的可能性较小,无效果编程会导致更少的错误(尽管不可否认,有时会出现新的/不同类别的错误)通过效果进行交互。

几乎没有人提到性能,并且无效果编程的性能通常比有效编程的性能更差,因为计算机是冯诺依曼机器,旨在与效果良好配合(而不是设计与效果良好配合)拉姆达)。 现在我们正处于多核革命之中,这可能会改变游戏规则,因为人们发现他们需要利用核心来获得性能,而并行化有时需要火箭科学家才能有效地获得正确的结果,当你没有效果时,很容易做到正确。

Side effects are a necessary evil, and one should seek to minimize/localize them.

Other comments on the thread say effect-free programming is sometimes not as intuitive, but I think that what people consider "intuitive" is largely a result of their prior experience, and most people's experience has a heavy imperative bias. Mainstream tools are becoming more and more functional each day, because people are discovering that effect-free programming leads to fewer bugs (though admittedly sometimes a new/different class of bugs) due to less possibility of separate components interacting via effects.

Almost no one has mentioned performance, and effect-free programming usually has worse performance than effectful, since computers are von-Neumann machines that are designed to work well with effects (rather than being designed to work well with lambdas). Now that we're in the midst of the multi-core revolution, this may change the game as people discover they need to take advantage of cores to gain perf, and whereas parallelization sometimes takes a rocket-scientist to get right effect-fully, it can be easy to get right when you're effect-free.

送君千里 2024-07-24 12:09:36

在冯诺依曼机器中,副作用是使机器工作的因素。 本质上,无论您如何编写程序,它都需要产生副作用才能工作(在低级别视图中)。

无副作用编程意味着抽象出副作用,以便您可以从总体上考虑问题,而不必担心机器的当前状态,并减少程序不同模块(无论是过程、类还是其他模块)之间的依赖关系。 通过这样做,您将使您的程序更具可重用性(因为模块不依赖于特定状态来工作)。

所以,是的,无副作用的程序是一件好事,但副作用在某种程度上是不可避免的(所以它们不能被视为“坏”)。

In von-Neumann machines, side effects are things that make the machine work. Essentially, no matter how you write your program, it'll need to do side-effects to work (at a low level view).

Programming without side effects means abstracting side effects away so that you could think about the problem in general -without worrying about the current state of the machine- and reduce dependencies across different modules of a program (be it procedures, classes or whatever else). By doing so, you'll make your program more reusable (as modules do not depend on a particular state to work).

So yes, side-effect free programs are a good thing but side-effects are just inevitable at some level (so they cannot be considered as "bad").

伴我心暖 2024-07-24 12:09:36

优点:

  • 最终,副作用就是你想要实现的目标。
  • 对于与外界交互的代码来说,副作用是很自然的。
  • 它们使许多算法变得简单。
  • 为了避免使用副作用,您需要通过递归来实现循环,因此您的语言实现需要尾部调用优化。

缺点:

  • 纯代码很容易并行化。
  • 副作用会使代码变得复杂。
  • 纯代码更容易证明正确性。

比如Haskell,一开始看起来很优雅,但后来你需要开始和外面的世界玩,就不再那么有趣了。 (Haskell 将状态作为函数参数移动,并将其隐藏到称为 Monad 的东西中,这使您能够以命令式的类似风格进行编写。)

Pro:

  • In the end side effects are what you want to accomplish.
  • Side effects are natural for code that interacts with outside world.
  • They make many algorithms simple.
  • To avoid using side effects, you need to implement loops by recursion, thus your language implementation needs tail call optimization.

Con:

  • Pure code is easy to parallelize.
  • Side effects can make code complicated.
  • Pure code is easier to prove correct.

For example Haskell, at first it seems very elegant, but then you need to start playing with outside world and it's not so much fun anymore. (Haskell moves state as a function parameter and hides it into things called Monads, which enable you to write in imperative look-a-like style.)

仙女 2024-07-24 12:09:36

副作用就像任何其他武器一样。 毫无疑问,它们很有用,但如果处理不当,可能会非常危险。

就像武器一样,你会产生各种不同种类、不同程度的杀伤力的副作用。

在 C++ 中,由于指针,副作用是完全不受限制的。 如果变量被声明为“私有”,您仍然可以使用指针技巧访问或更改它。 您甚至可以更改不在范围内的变量,例如调用函数的参数和局部变量。 在操作系统(mmap)的帮助下,您甚至可以在运行时修改程序的机器代码! 当你用 C++ 这样的语言编写时,你就被提升到了位神(Bit God)的地位,成为你进程中所有内存的主人。 编译器对您的代码进行的所有优化都是在您不滥用权力的假设下进行的。

在Java中,你的能力受到更多限制。 作用域中的所有变量都在您的控制之下,包括不同线程共享的变量,但您必须始终遵守类型系统。 尽管如此,由于您可以使用操作系统的子集以及静态字段的存在,您的代码可能会产生非本地效果。 如果一个单独的线程以某种方式关闭 System.out,这看起来就像魔术一样。 它是魔法:有副作用的魔法。

Haskell(尽管宣传是纯粹的)有 IO monad,它要求你在类型系统中注册所有的副作用。 将你的代码包装在 IO monad 中就像手枪的 3 天等待期:你仍然可以炸掉自己的脚,但除非你得到政府的同意。 还有 unsafePerformIO 及其同类,它们是 Haskell IO 的黑市,给你带来“不问任何问题”的副作用。

Miranda 是 Haskell 的前身,是在 monad 流行之前创建的纯函数式语言。 Miranda(据我所知……如果我错了,请用 Lambda 微积分代替)根本没有 IO 原语。 唯一完成的 IO 是编译程序(输入)、运行程序并打印结果(输出)。 在这里,你拥有完全的纯洁。 执行顺序完全无关。 所有“效果”对于声明它们的函数来说都是局部的,这意味着代码的两个不相交部分永远不会互相影响。 这是一个乌托邦(对于数学家来说)。 或者相当于 distpia。 这很无聊。 什么也没有发生。 您无法为其编写服务器。 你不能在里面写操作系统。 你不能在里面写SNAKE或Tetris。 每个人都只是坐在旁边看着数学。

Side effects are just like any other weapon. They are unquestionably useful, and potentially very dangerous when improperly handled.

Like weapons, you have side effects of all different kinds of all different degrees of lethality.

In C++, side effects are totally unrestricted, thanks to pointers. If a variable is declared as "private", you can still access or change it using pointer tricks. You can even alter variables which aren't in scope, such as the parameters and locals of the calling function. With a little help from the OS (mmap), you can even modify your program's machine code at runtime! When you write in a language like C++, you are elevated to the rank of Bit God, master of all memory in your process. All optimizations the compiler makes to your code are made with the assumption you don't abuse your powers.

In Java, your abilities are more restricted. All variables in scope are at your control, including variables shared by different threads, but you must always adhere to the type system. Still, thanks to a subset of the OS being at your disposal and the existence of static fields, your code may have non-local effects. If a separate thread somehow closes System.out, it will seem like magic. And it will be magic: side effectful magic.

Haskell (despite the propaganda about being pure) has the IO monad, which requires you register all your side effects with the type system. Wrapping your code in the IO monad is like the 3 day waiting period for handguns: you can still blow your own foot off, but not until you OK it with the government. There's also unsafePerformIO and its ilk, which are Haskell IO's black market, giving you side effects with "no questions asked".

Miranda, the predecessor to Haskell, is a pure functional language created before monads became popular. Miranda (as far as I've learned... if I'm wrong, substitute Lambda Calculus) has no IO primitives at all. The only IO done is compiling the program (the input) and running the program and printing the result (the output). Here, you have full purity. The order of execution is completely irrelevant. All "effects" are local to the functions which declare them, meaning never can two disjoint parts of code effect each other. It's a utopia (for mathematicians). Or equivalently a distpia. It's boring. Nothing ever happens. You can't write a server for it. You can't write an OS in it. You can't write SNAKE or Tetris in it. Everyone just kind of sits around looking mathematical.

小鸟爱天空丶 2024-07-24 12:09:36

如果没有副作用,您就无法做某些事情。 一个例子是 I/O,因为根据定义,使消息出现在屏幕上是一种副作用。 这就是为什么函数式编程的目标是最小化副作用,而不是完全消除副作用。

抛开这一点不谈,在某些情况下,最大限度地减少副作用与其他目标(例如速度或内存效率)发生冲突。 其他时候,您的问题已经有了一个与变异状态的想法非常吻合的概念模型,与现有模型作斗争可能会浪费精力和精力。

Without side-effects, you simply can't do certain things. One example is I/O, since making a message appear on the screen is, by definition, a side-effect. This is why it's a goal of functional programming to minimize side-effects, rather than eliminate them entirely.

Setting that aside, there are often instances where minimizing side-effects conflicts with other goals, like speed or memory efficiency. Other times, there's already a conceptual model of your problem that lines up well with the idea of mutating state, and fighting against that existing model can be wasted energy and effort.

云雾 2024-07-24 12:09:36

确实,正如这里有些人提到的,如果没有副作用,就无法做出有用的应用程序。 但由此可见,以不受控制的方式使用副作用并不是一件好事。

考虑以下类比:具有没有分支指令的指令集的处理器绝对毫无价值。 然而,这并不意味着程序员必须一直使用goto。 相反,事实证明,结构化编程和后来的 OOP 语言(例如 Java)甚至可以不需要 goto 语句,而且没有人错过它。

(可以肯定的是,Java 中仍然有 goto - 现在称为 breakcontinuethrow。)

It is true, as some people here mention, that without side effects one cannot make a useful application. But from that it does not follow that using side effects in an uncontrolled way is a good thing.

Consider the following analogy: a processor with an instruction set that had no branch instructions would be ansolutely worthless. However, it does not follow that programmers must use gotos all the time. On the contrary, it turned out that structured programming and later OOP languages like Java could do without even having a goto statement, and nobody missed it.

(To be sure, there is still goto in Java - it's now called break, continue and throw.)

池予 2024-07-24 12:09:36

副作用对于大多数应用的重要部分来说是必不可少的。
纯函数有很多优点。 它们更容易思考,因为您不必担心前置条件和后置条件。 由于它们不改变状态,因此更容易并行化,随着处理器数量的增加,这将变得非常重要。

副作用是不可避免的。 只要它们比更复杂但纯粹的解决方案是更好的选择,就应该使用它们。 纯函数也是如此。 有时,使用功能性解决方案可以更好地解决问题。

一切都很好 =) 您应该根据要解决的问题使用不同的范例。

Side effects are essential for a significant part of most applications.
Pure functions have a lot of advantages. They are easier to think about because you don't have to worry about pre and post-conditions. Since they don't change state, they are easier to parallelize, which will become very important as the processor-count goes up.

Side effects are inevitable. And they should be used whenever they are a better choice than a more complicated but pure solution. The same goes for pure functions. Sometimes a problem is better approached with a functional solution.

It's all good =) You should use different paradigms according to the problem you're solving.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文