Clojure China

我在 ClojureScript 的 2017

#1

2016 年我在 React 社区就不是非常活跃了, 虽然还会搬运一下新闻, 也密切关注着 Jordan Walke 的消息, 但是对于 React 社区的新技术很少去上手用了. 再说自己搞了 Respo, 从此怼 React 怼 Vue 就有了资本, 脾气当然是更不温顺了. 我自认为是 ClojureScript 社区的成员, 这一点很久都不会变了.

Cirru Editor

不过, 虽然我写 ClojureScript, 但是我并不用 EMACS, 或者其他 Clojure 用户爱用的 IDE. 从一开始, 我的热情就是 Cirru 的方案, 直接用网页编辑 S-Expression, 再依靠程序生成文本的代码. Clojure 有着强大的宏, 实际上实现起来反而比 JavaScript 不要简单太多了. 于是我这么做了, 搞得我很孤僻的样子.

最开始 Clojure 社区对我表示赞同和鼓励的人是 Shaun, 他是 Parinfer 的作者. Parinfer 是一个怼 Clojure 代码进行自动格式化的工具, 就像 JavaScript 这边的 Prettier, 虽然当时 Prettier 还没开发呢. Parinfer 发布的时候还上了 Hacker News, 赚了 600+ 点赞. 然后我发现在底下有个 “acknowledgements” 部分, 里面把 Cirru 列进去了:

Cirru Sepal in Clojure has an interesting approach to inferring Clojure parens from indentation and other syntax sugar— $, $ [], $ {} and ,.

那个是早期的 Cirru 的文本的语法, 基于缩进的版本. 因为 Parinfer 发布挺早的. 后来, 到了今年, Shaun 在 Clojure Conj 有个演讲 Inspiring a future Clojure editor with forgotten Lisp UX - Shaun Lebron, 为此他跟我沟通了一遍关于 Cirru 的细节. 虽然因为意外没有深入的参与, 但是渐渐我发现 Cirru 探索的方向算是得到了他的认可和鼓励, 并且近期又被引用了一次, 这次感觉真是美滋滋的了.

shadow-cljs

夏天的时候我对 ClojureScript 真的觉得不耐烦了, 忘了具体的肇因, 就是很郁闷. 毕竟等了很久, Webpack 的生态在逐渐加强, 而 ClojureScript 我连一个舒服的打包工具都用不了. 特别是不能生成像 Webpack 那样生成文件名 Hash, 以及做 Code Splitting. 我在冷清的邮件列表开了帖子, 要是英语够好简直想骂街了. 这时候遇到了 Thomas Heller, 跟我解释了半天说办法是有的. 最后我就向他要文档了.

当时 shadow-devtools 工具好像只有他一个人在用, 虽然有文档, 但是就像 Webpack, 鬼知道怎么配置啊. 于是我就一直在 Slack 上问他怎么用. 后来也不知道具体怎么发生的, 他直接把项目改成 shadow-cljs 然后开始加我需要的功能. 最早我想的是既然 Webpack 这么强大吗直接用就好了嘛, ClojureScript 编译成 CommonJS, 或者 ES6. thheller 就给了个 CommonJS 兼容的方案, 不过还是要用 JVM 的 ClojureScript 工具编译. 我想, 总比没有好.

后面 shadow-cljs 实际上提供了 npm 模块直接安装的, 我就不用配置 JVM 了, 关于这一点我是很意外的. 大概这是很大一个好感的来源吧.
于是最开始我就在 Webpack 里用起了 shadow-cljs 生成的 npm-module 格式代码了. 新项目毕竟坑多的, 不过还好 thheller 真是很有激情, 我在 GitHub 上开 issues, 经常隔夜就发现他修好了还加上了功能. 这样我反而不抱怨了. 渐渐地他说服了我, shadow-cljs 有很多 Webpack 能做的功能, 比如动态加载, 比如 Code Splitting, 比如监视文件热替换, 我渐渐也转移到他推荐的开发方案上来了.

那还是夏天, 挺顺利的. 后来因为我失恋, 又出去到处玩, 有一段时间就没经常上去看他的消息了. 直到后来偶然在 Slack 上聊起来, 他说在对付 npm 模块引用的问题, 他想在用 Webpack 帮助来做 npm 模块的打包, 想了几个月没有好办法. 打算试试其他的打包工具, 我跟他说了下 RequireJS, Browserify 那些破事, 他当时觉得挺好, 就想试一试. 可惜后来问题还是很棘手, 直到最后我都不清楚他是怎么解决的. 那段时间 ClojureScript 官方编译器也在处理 npm 模块打包的事情, 总之后来是给出了不错的方案了. 所以也就是秋天的事情, 浏览器模式的 ClojureScript 能引用 npm 模块了, 甚至在 shadow-cljs 里还提供了一下私有的方法.

总之事情过了半年, 我对 thheller 也是服了. 如果你查看 npm info shadow-cljs 的话, 除了七八月份他好像也经历了一些傻逼事情以外, 整个 shadow-cljs 几乎每天都在更新. 从最开始的打包工具, 一步步加入了各种 Webpack 当中实现过的先进的开发方案. 我嫌弃报错看不懂, 他于是加上颜色加上提示, 我嫌 JVM 冷启动太慢, 他优化了 server 模式然后说可以更快的, 然后也有了 Webpack 那样的 manifest.json 文件用于打包, 他自己还加上个简单粗暴 CSS 热替换. 这中间他的用户越来越多, 更多的功能也被优化起来了. 对我来说使用体验在追赶 Webpack 了.

起先的时候, 为了方便查询和传播, 我就注意留一些基础的教程和 demo, 写不来就催着 thheller 要文档. thheller 很忙懒得写文档, 但好歹也写了一些. 后来我上 Reddit 挺多的, 于是也帮着推销 shadow-cljs, 顺便对 boot-cljs 和 lein-cljsbuild 追加嘲讽. 安利的人多了, 然后就有人在 Reddit 上问 shadow-cljs 究竟怎么回事, theller 上去回答了, Shaun 跟 thheller 也有联系, 所以也去声援了. 我就觉得怼 boot 怼 lein 有望了. 至少从现在起这个山头就立在这了, shadow-cljs 是目前对 JavaScript 开发者最友好, 功能也最完善的 ClojureScript 编译工具.

Cirru Cumulo Editor

上半年的时候我还在用着 Stack Editor, 逐渐把存储文件迁移到了 ir.edn, 做了一些生成定义啦, 查找依赖啦, 各种的优化. 失恋加上离职那段时间有了空闲时间, 突然想到了一个解决实时协同编辑的一个方案, 比如一个列表, 我给每个节点生成有序的 id, 支持无限差值, 那么协同编辑冲突就能处理了, 于是集中精力实现了 bisection-key. 虽然基于已有的 Cumulo 同步方案, 重写了整个编辑器, 取名叫做 cumulo-editor.

我当时觉得真的是很强大的突破. 就在 SHLUG 的聚会上找人试用, 到了 JSConf 的时候带着 Pad 也给人看了. 自己觉得非常得意. 那段时间还录了不少 Cumulo Editor 的视频给人看. 刚开始就想着, 反正我离职了时间很多, 我继续加上功能, 也许还能找到试用的场景. 可是实际情况并没有想象当中美好, 我折腾了三周, 没啥效果, 虽然几个朋友给了好评, 但是我连陪我测试的人都没有.

八月份我肠胃炎之后心情更差了. 躲着烦恼去逛了下福建广州, 得到不少别的开导吧, 但是因为晒伤了后来就躲起来不爱见人了. 后面去阿里又面试, 在杭州触景生情, 想想还是赖在上海吧. 接着机会就调整节奏回来做 React 了. 不过 React 啊 Vue 啊折腾了几年, 我对 JavaScript 的好感已经没剩多少了, 我的思维习惯也切换到了 Clojure, 我用 Cirru Editor 写代码的顺畅程度远远高于 JavaScript. 当你自己搞出点东西的时候, 就算去大神鼻子底下装逼, 也是有底气的.

现在 Cumulo Editor 可以很方便地跳转到定义, 快速地搜索和切换函数, 表达式的复制剪切也挺方便的, 以及一些原始的重构功能. 放在很多年轻, 很难想象一个 DOM 编辑器处理代码能够方便到这种程度, 但是现在至少凭借一己之力我做出了点样子来, 而且我能看到后续有更多的改进.

Respo

由于编辑器也是依赖 Respo 的, 实际上 Respo 在我个人的场景当中被大量使用了. 因此中间也一直被改进着. 最有效而且明确的改进就是 defcomp 这个 Macro 的引入了, 实际上比如 div 这些标签也是通过 Macro 定义的. 为了批量定义出这些标签也花费了不少的心思. 一开始是 Macro 的调试, 关于 foo.core$macros 这个奇怪的写法我愣是找到 Lumo 作者问了找搞明白问题, 后来还给他们找到 bug 来了.

调试 Macro 需要一些经验, 虽然本身不是很难, 用 expandmacro-1 也足够, 但是一开始会有坑. 比如 symbol 被多次 evaluate 啦, 比如没有意识到需要 gensym 来避免变量重名啦, 或者 list 被意外处理成 eval 之类的. 经过足够多的例子, 算是那掌握好基本的用法了.

Respo 早一点还有一次大的改进, 就是对 state tree 的方案进行了简化, 或者说改成了半自动半手动的写法. 把原来基于节点定位的 state tree 改成了手动控制结构的 state tree, 导致了大量依赖 Respo 的项目都需要跟着更新. 包括我后来的其他 Respo 上的更改, 比如 list->, 也导致了很多的工作量.

从夏天开始我就注意到 Respo 相关的代码的适用范围越来越广, 在我个人项目当中涉及到的位置也是越来越多. 同时我的脚手架当中不断引入来自 shadow-cljs, Respo, Cirru 的更新, 维护的成本竟然是越来越高. 近期我还打算看一看 Cumulo 项目下有什么可以突破的地方, 需要处理的仓库算是更多了.

ClojureVerse

我有一些运营 CNode 和 React China 两个论坛的基础的经验, 到了 Clojure 这边少不了论坛, 我比较早拿到了 Clojure China 论坛版主的权限, 但是一直没起来. 我觉得是国内人太少. 后来看到国外有个现成的 ClojureVerse, 一样的 Discourse 系统, 于是要来了版主权限, 时不时就上去刷一刷.

在英语社区混的时间长了, 大致就熟悉了规律. 活跃的地方主要是 Clojurians 这个 Slack group. 如果英语好, 勉强是可以混进去的, 只是说时区不一样很难聊到一起. 然后就是 Reddit/Clojure 这个新闻站了. 虽然貌不惊人, 但是访问量是比较稳定的. 剩下 Twitter 上跟几个社区的核心开发者套近乎, 基本能看到对方发的进展. 几个人当中写博客最勤快的就是 Plank 的作者 Mike Fikes 了. 我甚至怀疑他就是目前 ClojureScript 的主力开发, 因为大量的优化都是看他做的. 而且最近这拨 REPL 的改进, 几乎都是他在引领.

另外还有 Google Groups, 也就是邮件列表. Clojure 的邮件列表还算活跃的, 不过 ClojureScript 的差多了. 再说我也不稀罕这么难用的编辑器和界面. 有好的东西, 就发到 ClojureVerse 上, Markdown, 多开心啊. 而且站长 Plexus 真是挺 nice 的, 解答问题别提多细心了.

而且近期他觉得 ClojureVerse 有重新做起来的希望, 开始准备 Relaunch. 我也是服了他了, 论坛真的活跃起来了, 社区里的大牛好多过来串门的, 不知道他人脉什么情况. 我偶然看到老帖子上提了一下他的经历, 看上去是柏林那边的聚会的组织者… 总之跟国内比比真实牛逼了. 也好吧, 感觉有盼头了.

结尾

最近 Jordan Walke 真实蠢蠢欲动了, ReasonML 3 真的很抢镜. 以我敏锐的嗅觉看, ClojureScript 的竞争对手上场了. 本来我觉得 ClojureScript 这次优化好编译器的体验, npm 的短板也补上, 能痛快点干一脚 JavaScript. 但是前端圈 WebAssembly 之类各种新东西不断, 就没把 ClojureScript 放在眼里. 现在 Reason 3 新语法真的相当亮眼, 比 ClojureScript 传播起来快得多.

我是笃定的 FP 的支持者, ClojureScript 热不起来, 我用 ReasonML 开发也是期待中的, 那么我只能分出精力学 ReasonML 的. 目前 Reason React 只是给出了组件级别漂亮的方案. 等到全局状态和异步操作完善, 估计就需要投入了. TypeScript 虽然牛逼, 但是能有 React 原作者设计的语言更适合 React 吗.

回到 ClojureScript 社区这边的事情, 我既然花费了两年开发了基础组件, 肯定是期望能够形成上层建筑的. 我也会努力证明 ClojureScript 在提升开发效率方面有着强大的一面. 也许 Lisp 真的不适合给开发经验不足因而缺少自律甚至缺少 FP 理论基础的人玩, 但我已经看到了它强大的一面, 我就要把它掌握下来安利给别人. 再者说, ClojureScript 社区的几个开发者我总算混熟了点, 我知道他们能力多强, 也多有想法. JavaScript 社区虽然强大, 但多的是杂音, 多的是妥协, 很让人纠结. 等 WebAssembly 平台的语言起来, 不闹翻天才怪.

最后呢, 你想成为怎样的开发者, 你选将来成为哪个社区的核心开发者那样的人呢?

2赞
#2

本论坛年度最佳贴,文风有点王珢的感觉啦~
我也非常希望、同时也非常相信 clojurescript 能结合nodejs(前端)和go(后端)开出灿烂的花朵,希望能有机会合作和学习,明年不想上班了,也少打些游戏了,多投入些时间精力做些有价值、感兴趣的事儿~

#3

二月开始被thheller安利shadow-cljs,不知不觉跟了快一年的时间,现在工作中已经离不开shadow-cljs了。shadow-cljs成功了,希望能看到群主的respo大放异彩,加油!

1赞
#4

shadow-cljs 是 5 月份开发的亲, 看 npm info shadow-cljs

#5

我觉得我的汉语功底比王垠要好才对… 毕竟写了那么多文章练过.

#6

我就喜欢你这样的性格~哈哈……实事求是!嗯,真的~

1赞
#7

向群主大大学习 :heart_eyes: :heart_eyes: :heart_eyes:

#8

您说得对,2月的时候还叫 shadow-build

1赞
#9

Clojure作者从设计之初,就抓住了重点:JVM和JS。从他前期的工作来看(dotLisp Jfli FOIL Lisplets),作者是很有眼光的,而且一直在为“让Lisp更加接地气”而努力,真的挺好。

近些年,facebook等公司对OCaml的支持力度,这也是OCaml在FP语言里有点热的原因吧。但看到Clojure社区有这么多的公司在用,我觉得只要有持续维护的产品作为后盾是不成问题的。

加油,po主!

2赞