无状态编程的优点

我最近一直在学习函数式编程(特别是Haskell,但我也经历了关于Lisp和Erlang的教程)。 虽然我发现这些概念非常有启发性,但我仍然没有看到“无副作用”概念的实际方面。 它的实际优势是什么? 我试图用function思维来思考,但是有些情况看起来过于复杂,没有一种简单的方式来保存状态(我不认为Haskell的monads很容易)。

是否值得继续深入学习Haskell(或另一种纯粹的函数式语言)? function性或无状态编程实际上比程序更有效率吗? 以后可能会继续使用Haskell或其他函数式语言,还是应该只为了解而学习?

我关心的不是性能而是生产力。 所以我主要是问我在function语言上是否会比程序/面向对象/什么更有效率。

简单阅读函数式编程 。

无状态编程有很多优点,其中最重要的是multithreading和并发代码。 说穿了,可变状态是multithreading代码的敌人。 如果默认值是不可变的,那么程序员不需要担心一个线程在两个线程之间改变共享状态的值,所以它消除了与竞争条件有关的整个类的multithreading错误。 由于没有竞争条件,所以也没有理由使用锁,所以不可变性消除了与死锁有关的另一类整体错误。

这就是函数式编程很重要的一个重要原因,也许是function性编程序列中最好的一个。 还有很多其他的好处,包括简化的debugging(例如,function是纯粹的,不会在应用程序的其他部分中改变状态),与严重依赖于devise模式的语言相比,更简洁和expression的代码,更less的样板代码,以及编译器可以更积极地优化你的代码。

你的程序中有越多的程序是无状态的, 将更多的程序放在一起而不会有任何中断 。 无国籍范式的力量本身不在于无国籍(或纯洁),而在于它赋予你强大的, 可重复使用的function并将它们结合起来的能力。

你可以在John Hughes的论文“ Why Why Programming Matters (PDF)”中find一个很好的教程。

你会变得更有效率,特别是如果你select一个代数数据types和模式匹配(Caml,SML,Haskell)的函数式语言。

其他许多答案都集中在函数式编程的性能(并行)方面,我认为这是非常重要的。 但是,您确实具体询问生产力问题,因为您可以在function范例中比在命令范例中更快地编程相同的东西。

我实际上从个人经验中发现,用F#进行编程与我认为更好的方式相符,因此更容易。 我认为这是最大的区别。 我已经在F#和C#中编写了程序,而且F#中的“与语言打架”也很less。 您不必考虑F#中的细节。 这里有一些我发现我真的很喜欢的例子。

例如,即使F#是静态types的(所有types都在编译时被parsing),types推断会判断出你有什么types,所以你不必说出来。 如果它不能解决它,它会自动使你的function/类/无论通用。 所以你永远不必写任何通用的东西,这全是自动的。 我发现这意味着我花更多的时间来思考这个问题,而不是如何去实现它。 事实上,每当我回到C#,我发现我真的错过了这种types的推论,你从来没有意识到如何分散注意力,直到你不再需要做。

同样在F#中,不是编写循环,而是调用函数。 这是一个微妙的变化,但意义重大,因为您不必再​​考虑循环结构。 例如,这里有一段代码可以通过并匹配一些东西(我不记得是什么,它来自一个项目欧拉难题):

let matchingFactors = factors |> Seq.filter (fun x -> largestPalindrome % x = 0) |> Seq.map (fun x -> (x, largestPalindrome / x)) 

我意识到,做一个filter,然后在C#中的地图(这是每个元素的转换)将是非常简单的,但你必须考虑在一个较低的水平。 特别是,你必须自己编写循环,并有自己的明确的if语句,以及这些types的东西。 自从学习F#之后,我意识到我已经发现用function的方式编写代码更容易,如果你想过滤,你写“filter”,如果你想映射,你写“地图”,而不是每个细节。

我也喜欢|>操作符,我认为它将F#从ocaml和其他可能的语言中分离出来。 它是pipe道操作员,它可以让你“pipe”一个expression式的输出到另一个expression式的input。 它使代码遵循我的想法更多。 就像上面的代码片段一样,这就是说:“把因素sorting,过滤,然后映射。” 这是一个非常高的思维水平,你没有得到一个命令性的编程语言,因为你很忙,编写循环和if语句。 每当我使用另一种语言时,这是我最想念的一件事。

所以就在一般情况下,尽pipe我可以在C#和F#中编程,但是我发现使用F#更容易,因为您可以在更高级别上进行思考。 我认为,因为从函数式编程中删除了更小的细节(至less在F#中),所以我的工作效率更高。

编辑 :我在其中的一个评论中看到,你要求一个函数式编程语言中的“状态”的例子。 F#可以被写入命令式的,所以这里是一个直接的例子,你可以在F#中有可变的状态:

 let mutable x = 5 for i in 1..10 do x <- x + i 

考虑所有你花了很长时间debugging的难题。

现在,有多less错误是由于程序的两个独立组件之间的“意外的交互”造成的? (几乎所有的线程错误都有这种forms:涉及写入共享数据,死锁的比赛…此外,find对全局状态有一些意想不到的效果的库或者读/写registry/环境等常见的)将假定至less有三分之一的“硬虫”属于这一类。

现在,如果您切换到无状态/不可变/纯编程,所有这些错误消失。 你会遇到一些新的挑战(例如,当你想让不同的模块与环境交互时),但是像Haskell这样的语言,这些交互被明确地指定到types系统中,这意味着你可以只看types它可以与程序的其余部分进行交互的types的function和原因。

这是来自“不可变性”国际海事组织的巨大胜利。 在一个理想的世界里,我们都会devise出令人惊叹的API,即使事情是可变的,效果也会是局部的,有据可查的,“意想不到”的相互作用将被保持在最低限度。 在现实世界中,有很多API以无数的方式与全球的国家进行交互,这些是最有害的错误的来源。 渴望无国籍者有意去除组件之间无意/无意/幕后的互动。

没有状态,自动并行化代码是非常容易的(因为CPU是由越来越多的内核构成的,这非常重要)。

我刚才就这个问题写了一篇文章: 关于纯洁的重要性 。

无状态函数的一个优点是它可以预先计算或caching函数的返回值。 即使一些C编译器也允许你明确标记函数为无状态来提高它们的优化性。 正如其他人所指出的,无状态函数更容易并行化。

但效率并不是唯一的担忧。 一个纯函数更容易testing和debugging,因为任何影响它的东西都会被明确地声明。 而当用function语言进行编程时,习惯于尽可能less地使用“脏”(与I / O等)function。 以这种方式分离出有状态的东西是devise程序的一种好方法,即使在非function性语言中也是如此。

function性语言可能需要一段时间才能“获得”,并且很难向没有经历这个过程的人解释。 但大多数持续时间足够长的人终于意识到,即使最终不使用函数式语言,大惊小怪也是值得的。

无状态的Web应用程序是必不可less的,当你开始有更高的stream量

例如,出于安全原因,可能有大量用户数据不希望存储在客户端。 在这种情况下,您需要将其存储在服务器端。 您可以使用Web应用程序的默认会话,但如果您有多个应用程序实例,则需要确保每个用户始终定向到相同的实例。

负载平衡器通常具有“粘性会话”的能力,负载平衡器一定知道向用户请求发送哪个服务器。 但这并不理想,例如,这意味着每次重新启动Web应用程序时,所有连接的用户都将丢失会话。

一个更好的方法是在某些数据存储中将会话存储在Web服务器后面,这些日子里有大量可用的nosql产品(redis,mongo,elasticsearch,memcached)。 这样Web服务器是无状态的,但是您仍然有状态服务器端,并且可以通过select正确的数据存储设置来pipe理此状态的可用性。 这些数据存储通常具有很好的冗余性,因此几乎可以随时更改您的Web应用程序甚至数据存储,而不会影响用户。