关于函数式

此篇博文整理了我和 walkerlala 关于函数式相关问题的讨论

Vincent Chan

我觉得函数式很重要的一点是 Immutable,使得每一次更改都要重新生成一次整个数据结构(当然我们可以通过编译器优化来减少某些生成),但是immutable 也使得整个程序的状态可以被记录和更改。我之前写前端的时候就尝试了 Redux + React 这种模式。这种模式很有意思,我刚写起来的时候觉得超级别扭,写了很多无用的代码。但是当我开始着手用它来写一个前端项目的时候,我开始体会到了这里面的思想(虽然我仍然觉得很恶心和别扭,因为它尝试用 js 来写函数式)。

我们都知道 html 界面其实就是一颗很庞大的树,有很多 html 节点,这些html 节点里面有很多子节点,每个子节点又有自己的属性(attributes)。当我们以前写的 jQuery 代码都在干什么?就是绑定一个节点的事件,然后更改另一个节点。想想看,我相信你写过 jQuery,我们先在这个庞大的 document 树里面 query 到节点,比如一个 button,然后我们监听它的click 事件,当这个事件发生时,我们 query 另外一个节点,比如一个label,然后修改里面的内容。这就是我们以前用 jQuery 做的事情。

而 Redux+React 的事情是这样的,React 维护着一个 js 的假的 document tree,这棵树和真正的 document tree 比起来是很轻量级的,也很快,然后通过这颗虚拟的树去生成真的 document 树。每当一个状态改变了,然后,重新生成一颗虚拟树,然后和老的虚拟树做 diff,然后更新真的 DOM。这种做法看起来很折腾,但是却有它的用处:什么状态就对应怎样的 DOM,也就是你状态没错,你界面肯定就没错。想想之前 jQuery 的办法,逻辑多起来,你怎么保证状态一定是你脑中的那样。那我们怎么管理这个状态呢?这里就用 Redux 了:

每当我们要改变一个状态时,我们就是要 sending a message,这个message 是一个数据,告诉 redux 我要更改什么,删除什么,添加什么……然后我们会定义一个 reducer,reducer 的作用就是通过一颗旧的状态树,接收消息之后,通过消息来生成一颗新的状态树,然后把新的状态树交给React 生成新的虚拟 document 树,然后再做 diff,然后更新视图。

刚才 jQuery 做的事情,我现在用 Redux+React 的方法重新做一遍:把某个按钮绑定某个 meesage,当用户按下某个按钮,就发送一个 message,告诉 Redux 要更新某个 label 的值,Redux 根据 message 重新生成新的状态树,包含了新的 label 值,然后生成新的虚拟 DOM 树,然后和旧的比较,做 diff,然后更新真正的 DOM。

当你开始用 Redux+React 重新写上面的流程的时候,你会沉浸在很多细节里面,因为要解决很多问题,但是你简化这个模型,发现这就是 FP 的思想,而这种思想来源于 Elm,这是一个 Haskell 写成的语言,语法是仿Haskell 的,可以编译成 js 代码,而这门语言所实现的,就是刚才所说的Redux+React 架构,当我们用 Elm 写上面这一切的时候,你会发现,这种做法用FP语言去做是多么自然,React+Redux 几百行代码才做好的事情,用Elm可以用不到一百行写出来,建议你去官网看看,代码真的非常漂亮。我觉得写 Redux+React 简直就是在人工编译 Elm 代码。

至于为什么 Elm 没有火起来,而 Redux+React 火了,我相信你也明白,Redux+React 是用 js 写的,可以和现有的代码很好的结合,而 Elm 要重写代码,Haskell 语法一般人也很难接受,加上 React 有 Facebook 的爹(摊手

Yubin Ruan

Hmm... 对我来说,FP 的 Immutable 的好处有两种:

  1. 因为没有 side effect 所有比较好 reasoning;
  2. 因为看起来像看数学公式一样所以逻辑清晰(同时也方便形式化验证)。

但是你上面所说的状态,真的也是 FP 的好处吗?即使是一个状态对应一个虚拟树,但是如果逻辑多起来(e.g., 状态A => 状态B => 状态C),怎么保证状态一定是脑海中的那样呢?JQuery 的确有这个问题(状态比较乱),但是 Elm 貌似也治标不治本...?

当然,如果 Elm 是内嵌 Redux+React 的模式的话,肯定是比 jQuery 要好。

我其实写过很多 JS 代码,虽然没有正式看过他的语法(至今还分不清 === 和 == 的区别)。给我的感觉的确很操蛋...大括号换行还能改变程序的意思;闭包是假闭包,而且你还不知道这个闭包是真的还是假的(C++ 的闭包虽然可以非 Immutable,但是起码都会在闭包声明的时候看得到)...

我觉得终究会有一门语言来操翻 js 的 (摊手

诶,如果C语言里面有 hygien macro 我觉得就完美了。