函数式编程,想说爱你不容易

Posted 技术风向标

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数式编程,想说爱你不容易相关的知识,希望对你有一定的参考价值。

我看到不少人对于函数式编程没有流行起来感到很不解,就比如我最近正在读的《走出陷阱》一书中作者先为函数式编程辩解一番,之后又接着说:


然而,事实上这样的辩解并不足以让函数式编程得到广泛使用,我们得到的最终结论是,函数式编程的主要弱点正是它的强项——也就是说当系统必须保持在某种特定状态的时候,问题就出现了。


我认为函数式编程缺乏人气的原因很简单:编写函数式代码时思维通常是向后(backwards)的,这种方式更像解谜,而不是向计算机解释一个过程。在函数式语言中,虽然我知道自己想说什么,但为了想清楚如何用编程语言表达它而又得费劲解半天谜。函数式编程太怪了!

烤蛋糕和函数式语言
函数式编程,想说爱你不容易

为了说明函数式语言,我们先看一看如何烤制一个蛋糕。先来看看如何烤制一个“命令式”的蛋糕:

1. 将烤箱预热至175℃。将黄油擦到2—8英寸的平底锅里。在一个小碗里搅拌面粉、小苏打和盐;备用。

2. 在一个大碗里,将黄油、白糖和红糖打到发亮蓬松状态。打入鸡蛋,一次一个。混入香蕉。轮流加入面粉和脱脂牛奶到这个蓬松的混合物中。混入剁碎的核桃。最好倒入平底锅。

3. 在预热好的烤箱中烘焙30分钟,然后从烤箱中拿出来,放到一个微湿的毛巾上冷却。

以上我列出了3个步骤(显然每一步都是由若干小步组成)。现在我们看一下如何烘烤一个“函数式”的蛋糕:

1. 蛋糕是一块要用微湿的毛巾冷却的热蛋糕。这个热蛋糕是一块需要在预热好烤箱里烘烤30分钟的蛋糕生坯。

2. 预热好的烤箱是指一个预先加热到175℃的烤箱。

3. 蛋糕生坯是指被倒入平底锅中的糊状物。而糊状物是指混入了碎核桃的混合物。而这个混合物又是指在一个大碗中被打到发亮蓬松状态的面粉、红糖和白糖。

函数式编程,想说爱你不容易

这样解释下去没完没了!我不知道如何在不使用可变状态的前提下解释这些步骤,我可能说着说着就不小心打乱了顺序,也可能会说“然后混入香蕉”这种话。谁有兴趣在评论里把上面这堆“函数式”烤蛋糕步骤写完?


命令式的语言具有隐含的状态,这是一个巨大的好处,人和机器都喜欢在特定时间下有一个明确的状态。你读了蛋糕食谱就知道在做完第一步的时候,烤箱已经准备好了,平底锅已经擦上油了,我们也混合好了一个糊状物,这不需要明确的说明。我们按指令行事,并且知道执行完指令之后的结果状态是什么样子,没人会对命令式的食谱感到疑惑。如果我真的用“函数式”的方法完成了食谱,并把它交给我妈妈,她该崩溃了。(至少在不使用monads的版本中是非常混乱的,使用monads的版本可能会好一点)
函数式编程,想说爱你不容易
我遇到的问题
函数式编程,想说爱你不容易

我写这篇文章是因为最近我遇到了一个相关的问题。C++模板有时候是函数式语言,语言设计者没有很好的解决这个问题,这导致将其代码转换为通用代码的时候非常麻烦。


如下是我最近写的一个语法分析器:(我知道自己写语法分析器是挺蠢的,但是那些旧的工具像yacc或bison都很不好用,而当我试着用boost spirit时又遇到一些问题,这些问题我花了很长时间才弄明白,总之最后我决定自己写一个语法分析器……)

函数式编程,想说爱你不容易
这个函数通过验证A,B,C,D四个类型来确定变量的类型。这里有一些很明显的重复:这四个不同的分析器使用的是完全相同的代码。C++中不支持monad模式,但我们可以通过循环来重复使用代码,达到依次验证这四个类型的目的:

函数式编程,想说爱你不容易

这里有一些额外开销,因为我得返回一个错误信息,但总体而言,这是一个持续向前的运行过程。但是这在C++中无法做到。一旦涉及到模板,你就要考虑更多的函数。如下是我的解决办法:

函数式编程,想说爱你不容易
对这个解决办法很满意。它很难读,因为它的循环隐藏在递归里,但是你应该看到了在找到这个解决办法之前我有什么:我有一个包含有

<std::reference_wrapper<Parser<T>>…>的std::tuple结构。如果你用过可变大小的std::tuple,你就会知道那会把任何代码都变成一个谜。

函数式编程,想说爱你不容易 
总结

这里重点是我可以使用直接的命令式代码来多次进行相同的事情。为了使其通用,我不能仅仅只是在重复代码的前后引入一个loop循环,而必须完全改变控制流程,这就有太多问题要解决。


实际上我第一次尝试这个问题的时候也没能解决它。在第一次尝试中,我仅仅做了一些非常简单的事情,而大部分的原始代码并没有改动。而在几天后我重新回到这个问题上时,我才得到了如上的简单解决办法。使代码通用不应该是这么复杂的,这里所做的工作不是要指出应该做什么,而是试图指出如何满足系统的要求。


在函数式语言中我经常碰到这个问题。我们都知道C++模板是一个不好的函数式语言,但是即使是在好的函数式语言中,我也要花费很多时间来考虑如何说事情,而不是考虑到底要说什么事情。


你们认为我是在说函数式编程不好吗?并不是。函数式编程的优点也是真实和有价值的。每个人都应该学习至少一种函数式编程语言,并且尝试在其中应用从其它语言学到的东西。但如果函数式编程语言想要流行起来,还是最好别老让人玩解谜游戏。

推荐阅读


以上是关于函数式编程,想说爱你不容易的主要内容,如果未能解决你的问题,请参考以下文章

函数式编程,真香

“完全”函数式编程

Java 8 新语法习惯 (更轻松的函数式编程)

函数式编程

一文读懂函数式编程

JS中函数式编程基本原理简介