副作用是好事吗?

Léo*_* 준영 28 procedural-programming functional-programming side-effects

我觉得这个词很贬义.因此,我对维基百科中的两句话感到惊讶:

已知命令性编程用于使用副作用来使程序起作用.反过来,功能编程因其副作用的最小化而闻名.[1]

由于我有点数学偏见,后者听起来很棒.副作用的论据是什么?它们是指失去控制还是接受不确定性?他们是好事吗?

Nor*_*sey 52

我经常看到一个关于SO的问题迫使我花半个小时编辑一篇非常糟糕的维基百科文章.这篇文章现在只是中等不好.在关于你的问题的部分,我写了如下:

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

副作用对于使程序与外部世界(人,文件系统,网络上的其他计算机)进行交互至关重要.但是副作用的使用程度取决于编程范式.对于不受控制的混杂使用副作用,已知命令式编程.在函数式编程中,很少使用副作用.标准ML和Scheme等功能语言不会限制副作用,但程序员习惯于避免它们.功能语言Haskell使用静态类型系统限制副作用; 只有产生IO类型结果的函数才会产生副作用.

  • +1导弹:http://en.wikipedia.org/w/index.php?title = Side_effect_%28computer_science%29&action =historysubmit&dif = 294241985&oldid = 294094747 (19认同)

Bri*_*ian 25

副作用是必要的邪恶,人们应该寻求最小化/本地化它们.

关于线程的其他评论说,无效编程有时不那么直观,但我认为人们认为"直观"的主要原因在于他们以前的经验,而且大多数人的经历都有严重的命令性偏见.主流工具每天都在变得越来越实用,因为人们发现无效编程导致更少的错误(尽管有时候肯定是新的/不同类型的错误),因为单独的组件通过效果进行交互的可能性较小.

几乎没有人提到性能,并且无效编程通常具有比有效性更差的性能,因为计算机是von-Neumann机器,其设计用于效果良好(而不是设计为与lambda一起工作).现在我们正处于多核革命之中,这可能会改变游戏,因为人们发现他们需要利用核心获得性能,而并行化有时需要火箭科学家来充分发挥作用,当你没有效果时,很容易就能做到.

  • 无效编程可以像有效编程一样高效(参见mlton.org或caml.inria.fr),但编译器编写者必须更加努力才能做到这一点. (3认同)

Meh*_*ari 17

在von-Neumann机器中,副作用是使机器工作的东西.从本质上讲,无论你如何编写程序,它都需要做副作用才能工作(在低级视图中).

没有副作用的编程意味着抽象出副作用,这样你就可以一般地思考问题 - 而不用担心机器的当前状态 - 并减少程序的不同模块之间的依赖关系(无论是程序,类还是其他任何东西).通过这样做,您将使您的程序更可重用(因为模块不依赖于特定的状态来工作).

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


Joh*_*ith 9

优点:

  • 最后,副作用是你想要完成的.
  • 对于与外界交互的代码,副作用很自然.
  • 他们使许多算法变得简单.
  • 为避免使用副作用,您需要通过递归实现循环,因此您的语言实现需要尾调用优化.

缺点:

  • 纯代码易于并行化.
  • 副作用会使代码变得复杂.
  • 纯代码更容易证明是正确的.

例如Haskell,起初看起来非常优雅,但是你需要开始玩外面的世界,它不再那么有趣了.(Haskell将状态作为函数参数移动并将其隐藏在称为Monads的东西中,这使您能够以命令式的外观类型进行编写.)

  • 副作用使算法变得简单,因为它们非常强大,并且应该谨慎使用强大的工具。 (2认同)
  • 我认为,与"复杂"相比,更少和更模块化更清晰,更具体. (2认同)

Pil*_*lsy 7

没有副作用,你根本无法做某些事情.一个例子是I/O,因为根据定义,在屏幕上显示消息是副作用.这就是为什么函数式编程的目标是尽量减少副作用,而不是完全消除它们.

除此之外,通常会将副作用最小化与其他目标冲突,例如速度或内存效率.其他时候,已经存在一个问题的概念模型,该模型与变异状态的想法很好地吻合,并且与现有模型作斗争可能会浪费精力和精力.


Tac*_*ics 7

副作用就像任何其他武器一样.它们毫无疑问是有用的,并且在处理不当时可能非常危险.

像武器一样,你有各种不同程度杀伤力的副作用.

在C++中,由于指针,副作用完全不受限制.如果变量声明为"私有",您仍然可以使用指针技巧访问或更改它.您甚至可以更改不在范围内的变量,例如调用函数的参数和本地.在OS(mmap)的帮助下,您甚至可以在运行时修改程序的机器代码!当您使用C++这样的语言编写时,您将升级到Bit God的级别,掌握您进程中的所有内存.编译器对代码进行的所有优化都是在假设您不滥用权限的情况下进行的.

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

Haskell(尽管有关于纯粹的宣传)有IO monad,它要求你用类型系统注册你所有的副作用.将你的代码包装在IO monad中就像是手枪的3天等待时间:你仍然可以自己踩脚,但直到你和政府合作才行.还有不安全的PerformIO及其同类产品,这是Haskell IO的黑市,给你带来"无问题"的副作用.

米兰达是Haskell的前身,是一种纯粹的功能性语言,在monads变得流行之前就已经创建了.米兰达(据我所知......如果我错了,替代Lambda Calculus)根本没有IO基元.唯一完成的IO是编译程序(输入)并运行程序并打印结果(输出).在这里,你有完全的纯洁.执行顺序完全无关紧要.所有"效果"都是声明它们的函数的局部,意味着两个不相交的代码部分永远不会相互影响.这是一个乌托邦(对于数学家而言).或者等同于一种歪曲.这很无聊.什么都没发生.你不能为它编写服务器.你不能在其中编写操作系统.你不能在其中写入SNAKE或俄罗斯方块.每个人都只是坐着看数学.


小智 6

陪审团还在外面.自计算机开始以来,审判一直在进行,所以不要期待很快就能做出判决.