使用JavaScript实现“真·函数式编程”-2

401 查看

上一篇文章使用JavaScript实现“真·函数式编程”本来以为可以一次性写完的,结果话痨本色,没办法,继续填坑,这篇应该可以完结了,讲道理嘛。

这篇当中将介绍如何在纯函数式的限制之下实现“局部变量”和“状态”。

2. 实现局部变量

首先考虑这样一段代码

这是一段典型的命令式编程的代码,它最主要的问题就是局部变量name

在上一篇文章的第一个实现对数组遍历的例子当中我们已经对“顺序执行”初窥门径,通过构造了一个two_steps函数,实现两个步骤(函数)顺序执行。

在这个构造过程当中,我们得到一个重要的思路,就是在函数式语言当中,如果你想“获得”一个什么东西,就构造一个新的函数,把它参数化。对于two_steps的例子而言,“想获得”的是一个step2,就把它参数化了。

所以当需要“获得”局部变量的时候,自然而然我们会想到,把要拿的东西参数化就OK了,于是我们可以简单的这么构造:

local函数接收两个参数,a是要“捕获”的值,f是接收或者说消费这个值的函数,用它来改造上面的代码

上文当中将getName()的结果“捕获”作为后面函数的参数,实现了“局部变量”name。把上面的函数按照“真·函数式编程”规则改写:

不难发现我们这个local其实就是two_steps的简化版,区别在于two_steps的第一个step1是一个函数,而local则是一个值,如果用two_steps实现local那么就是:

看,我们这个local的风格,看起来非常像JS当中的“回调”的方式——事实上,因为像Haskell这样的纯函数式语言没有顺序执行,你可以认为每一行代码执行顺序是不一定的,这非常类似于在JS中我们遇到了海量异步操作的时候:异步操作的执行顺序是不一定的,所以才会用回调函数来保证“异步操作->处理结果”这个顺序。回调是一种非常朴素,非常好理解,但写起来却反人类的异步编程方式。我一直不批判浏览器和node.js里把API都用回调风格来定义,因为它很原始,大家都懂,至于后来的如Promise这些方式,也可以用回调的API轻松封装出来,咸甜酸辣,五仁叉烧,任君挑选。

OK,扯远了,也许你觉得上面的例子太过简单,下面我们来看这篇文章中真正重点的内容。

3. 实现状态

以下的例子基本上都源自从陈年译稿——一个面向Scheme程序员的monad介绍文中搬运和改造,我从这篇文章获得了巨大的启发,也先对作者和译者表示感谢。

我们写程序的过程当中常常回用到自增ID这种东西,如果在JS里要实现一个自增ID,可能会这么写

好嘛,绕了一圈,又回到刚才的话题了,局部变量(这次在闭包里面而已,本质是一样的),和二次赋值。但是经过前文的启发很容易就能用参数化的方式来解决这个问题

也就是