导航
导航

函数式初识(1) - 为什么要用函数式编程(以JS为例)

首先需要说明的是,这篇文章是以JS为例的,而JS并不是一个以函数式设计的语言,它仅仅是可以做到函数式,那么既然如此我为何还要以JS为例呢?很简单,因为我是一名前端,目前我只会JS。第二个前提条件是,我们假设所有你参与的项目不可能是你一个人完成的。

函数式编程在React团队推出Hooks方案之后在前端领域算是掀起了一股小热潮,于是在各大网站上一些问题的热度突然就起来了,比如为啥要用函数式编程?而为了解释这个问题,就不得不提到两个编程思想。

命令式编程(Imperative Programming) vs 声明式编程(Declarative Programming)

命令式编程更加注重于如何(how)去实现某件事。

例如给你一个数组求和,命令式的方法基本就是,声明一个变量total,然后来一个for循环,把每一个元素加到之前的total变量中,然后return。这种风格一步一步告诉计算机该怎么做,性能表现也相对优异。但是当过几天另外一个人来读这些代码的时候,他需要一行一行把代码加载到大脑里,然后人脑运算,得出结论,哦,这个for循环是求和。声明式的代码往往要强制让人们在自己的大脑中运行代码然后理解代码中的每一行做了什么事情,而运行代码显然不是我们人脑在行的,而这个运行代码的过程往往只是为了理解这些代码在干嘛。

而声明式的代码风格就不那么注重于如何去做,声明式的代码更注重于代码的产出(what)。还是以数组求和为例,声明式的代码基本就只有一句sum函数,传参,拿到结果。这个时候可能熟悉JS的同学会说,可是sum函数还是要你去实现啊。这就是为什么JS是支持声明式的而不是完全声明式的。一些纯函数式的语言比如Haskell会提供许多已经实现的功能,你需要做的就是讲这些功能组合起来,去得到你想要的结果,至于里面是怎么实现的,你可以不用关心,就如同你将渲染逻辑交给了React而不用关心他的实现是不是有什么问题。

可以看出声明式的代码可读性要强于命令式编程,原因是我们把运行代码这部分内容让更擅长的计算机来做了,我们需要做的仅仅是维护语义化很强的代码,我们不用在头脑里再运行一次代码,看这段代码究竟在干嘛。而说到代码维护,就又不得不提到注释

people always says, comments is a eval, if code has comments, that’s means the code is terrible. Code need to self document itself. I would say no, Code doesn’t need to self document itself. Code needs to tell a story, it needs to communicate, that’s for true.

There is a purpose for code comments. They’re not an excuse for not caring about your code. A lot of people will go to the other extreme and say, what does it matter how bad the code as long as there’s a code comment, and I explanined what I was doing.

A bad code comment is worse than no comment at all. You don’t have to remove all comments, but your comment should not duplicate the narrative of what the code is doing. comments should explan why, not how, sometimes maybe it needs to focus on the how if it’s particularly confusing, if theres’s something particularly hard to understand. what if your code made it obvious why it was doing something.

所以好的声明式的代码读起来更像是一份文档,甚至是一篇故事,你只有在发现故事无法进行下去的时候才需要去加一些注释。

所以说了这么多,我们为什么用函数式编程呢?一个比较大的原因是因为函数式编程相对于其他的编程范式来说要“更加声明式”,读起来更加“自然”。它把代码运行层面的东西都交给了计算机(所以函数式也经常被人吐槽性能问题,比如immutable),人们可以把自己的精力放在代码的逻辑和维护上面来,用人话说就是,可读性相对更强。

可读性

这个时候一定会有人吐槽:兄嘚,别忽悠了,我们组张三在组里推函数式,他那代码,我打包票,三天后他自己都看不懂。

兄弟,别急,坐下来喝口水,我们继续来看看这张图:

readability-for-FP

这张图我们假设一个人是从命令式转到声明式来的,横轴是转换过程中所处的阶段,纵轴是这个人所写代码的可读性。然后我们来解读一下这张图:

首先在0点位置,作为程序员我们假设大家写的代码都是有一定的可读性的,这个时候他想把自己的代码风格转换成声明式的,于是他的代码会有一段时间的可读性增长。就像是你在JS里第一次接触了foreach、map、reduce一样,你会开始逐渐抛弃for循环。记得刚进公司的时候我跟同事探讨过这个问题,为什么你不喜欢for循环了?得到的答复基本上是一致的,这些原生提供的方法写起来“更舒服”,读起来也更容易理解代码在做什么。虽然这些方法不算是函数式,但是也已经向声明式进了一步了。

这个时候你一般会觉得很酷,想把更多的东西写成声明式的,然后噩梦就开始了,你的代码开始不可控。为啥呢?因为你想要吧自己的代码“变成”函数式的,但是函数式对你来说算是一种没有接触过的编程范式,你为了能够把自己的代码“套进”函数式的原则里,开始大量牺牲可读性,并且沾沾自喜,我会写函数式的代码了~然后同事开始质疑你写的这是啥,自己也发现渐渐看不懂自己的代码了,说好的可读性更强呢?明明是更烂了。

于是有一大批人在这个节点放弃了,并且加入了吐槽的大军中去,函数式可读性高?高个P,写出来的全是意大利面条。另外一部分人坚持下去了,并且开始逐渐理解函数式,在加强的函数式的各种理解后,代码的可读性终于在谷底反弹,并渐渐超越了过去的自己。

小黄书的作者Kyle用了3年让自己成为了一个函数式的工程师,他说自己在这中间也想过要放弃,在中间位置的部分比你想象的还要糟糕,所以如果你也想要尝试,那么请不要放弃,这不是一朝一夕能够改变的。