我开发了一个基于 Beancount 的账本托管服务 HostedBeans,欢迎大家来了解纯文本复式记账并试用我的服务。

Atom 体验报告

回想起来,大概是去年这个时候,Github 宣布开始开发 Atom, 当时 V2EX 上到处都有人在求邀请码。因为 Github 并没有严格地限制下载和使用,所以虽然我当时没有邀请码,但也试用了一下 Atom. 当时的感觉是性能非常差,Bug 非常多,非常难用,于是就放下了。

后来 2014 年末,大概是听说 Atom 要发布 1.0 版本了(虽然到现在仍未发布),于是又下载试用了一个月左右,发现已经很少遇到 Bug 了,于是彻底从 WebStorm 迁移到了 Atom, 后来几个月变成了 Atom 的脑残粉。

喜欢 Atom 并不是因为它已经有多么好用了,直到现在 Atom 也仍然存在很多问题,我更看好的是 Atom 的设计理念和它未来的发展。Atom 希望兼具易用性和可编程性,它希望既可以对第一天学习编程的学生展现出易用性,又可以对一个代码大师展现出强大的可编程性。

Atom 的第一个特点是它构建于 Web 技术之上,最近几年 Web 技术已经发展成了一种新的「操作系统」,具有一套完整的 API 来加速应用的开发,几乎颠覆了传统的应用开发模式。Atom 构建于 Chromium 和 Node.js 之上,Chromium 是最流行的浏览器之一,借助 Web 技术可以直接通过 HTML 来布局,用 CSS 来影响界面的样式,因为 Atom 使用内建的 Web 引擎,所以可以直接使用新的 Web 技术,不必顾及浏览器兼容性等琐碎细节。Node.js 则赋予了 JavaScript 使用 C++ 拓展、完整地访问网络和文件系统的能力,并且可以直接使用 NPM 上已有的丰富资源。

Atom 的第二个特点是它使用了一种完全插件化的设计,Atom 的核心仅仅是一个管理插件的框架(核心仅 15000 行代码),任何「有意义」的功能都被以插件的形式实现,实际上 Atom 内置了大概 70 个插件来实现诸如文件列表、设置面板、命令面板、查找替换之类的基本功能。作为一个通用的编辑器,不太可能直接面面俱到地考虑到各种需求,索性不如通过彻底地插件化来适应各种不同类型的开发任务。Atom 鼓励用户通过编程的方式去进行定制, Atom 具有设计良好的 API, 还有一份 非常美观的文档 被摆在了官网的显著位置。

最近一年我写 JavaScript 比较多,我发现对于 JavaScript 这种动态类型、动态作用域的语言,WebStorm 基于静态分析的自动补全费力不讨好,并不能准确地进行提示,索性不如直接用字符串模糊匹配的方式来进行自动补全,毕竟对于我这种重量级用户来说,是很清楚某个对象具有哪些属性的,补全只是为了加速。

和 Sublime Text 相比,Atom 的性能确实差了一截,大概和 WebStorm 差不多的样子。我觉得 Atom 在性能上还是有一些提升空间的,目前的卡顿主要是因为 JavaScript 是单线程的,遇到密集计算就会阻塞事件循环,至于解决方案自然就是把密集计算和 DOM 操作 分离成两个线程,但大概这项工作需要插件的开发者一同来配合,所以比较难以开展吧。

因为 Atom 以插件为中心,所以我也不好说它能做什么、不能做什么,总之目前 Atom 已经可以满足我的所有需求,Atom.io 为每个用户提供了一个用户页, 各位可以 在这里看到我使用的插件。在未来我希望 Atom 社区能出现更多好用的插件,将与开发相关的功能都集成进来,例如一个好用的终端、断点调试器、数据库查看器等等,我最近也在尝试自己编写插件,不过目前还没什么成果。

抽象的构件

最近在看「JavaScript 权威指南」和「计算机程序的构造和解释」,于是不由得对比起 JavaScript 和 Lisp. 之前看「黑客与画家」的时候,作者在极力地推销 Lisp, 认为因为它优良的设计,所以是世界上最好的编程语言。但是从结果来看,Lisp 依然是一个极其小众的语言(以至于 MIT 已经将 6.001 从 Lisp 换到了 Python)。

在我读 SICP 的过程中,我开始感叹这本书选择 Lisp 作为表现语言是十分明智的,如果换一个语言恐怕这本书就不会有这么大的影响力了。编程中最核心、最有挑战性的工作是「抽象」,通过抽象来控制复杂度,让构建数十万、数百万行的程序成为可能,而编程语言则负责提供抽象的手段。

按我的理解,Lisp 提供了三项非常基本,但十分强大的抽象手段:

  • 函数作为第一类值,可以作为参数传递,可以将动作抽象成数据
  • 变量在离开了作用域后依然可以被闭包使用,函数可以拥有内部状态
  • 序对作为最基本的数据结构,可以在两项数据之间建立联系

于是 SICP 才可以由这些基本手段开始,逐步介绍如何一层一层地构造更复杂的抽象。如果换成 Python 的话,一方面存在一些限制(无法像操纵数据一样操纵代码),另一方面各种复杂的抽象手段(类)和数据结构(数组和散列表)又让人眼花缭乱,抓不住「抽象」的基本单位。

但正因为 Python 在提供复杂抽象手段的同时,又提出了一些限制,才能被广泛地使用。因为很多人是没有系统地学习过「抽象」这项技能的,对于他们而言,编程语言必须直接提供强有力的抽象手段,而不能只提供基本的抽象构件。

由此我又想到了 JavaScript 一开始只用在浏览器里实现简单的动作,最近几年才开始被用来构建大型的单页应用和服务器端程序。于是开始有很多人抱怨用 JavaScript 实现面向对象很麻烦,甚至说 JavaScript 是一个设计得很不好的语言。实际上 JavaScript 的原型是要比传统的面向对象接口(类、继承、静态成员/实例成员、共有成员/私有成员、多态)更基本的构件,但有些人并不这么觉得,他们觉得 JavaScript 必须直接提供更高抽象程度的面向对象接口(其实 ES6 已经在这么做了)。

读代码是好的学习方式么

自我开始编程以来,我一直觉得读别人的代码的难度,要几倍于自己写代码。一直以来我都很困惑,难道是我技艺不精,所以读别人的代码很困难么。

其实不是,我能看懂代码中的每一句话,并没有我不认识的语法,但连在一起就不懂为什么作者要这么安排代码了。

后来我渐渐有了一些想法,代码是程序员给计算机的命令,是作者思考过后的产物,但思考的过程却没有体现在代码上,这就好比一道数学题,只有一个最终答案,所有的计算过程都被省略掉了,自然难以理解作者的意图。一段代码一开始写出来,后来发现存在问题,陆陆续续地改过好几版是很常见的事情。最终版本中可能每个小的细节,都是作者花了很多时间试错的结果,但这个试错的过程并没有直接地体现在代码上。

另一方面,代码中往往存在一些「隐含前提」,例如假定函数的参数已经在传入之前被以某种方式处理过了,这个假定很可能与另一个文件的某行代码有关,这种联系很难引起阅读者的注意。当然,好的设计可以缓解这个问题,但很难被彻底地解决。

代码的历史会被保存在版本控制系统里,但说实话,按我的经验,很少真的有人去翻版本历史,因为正确地使用版本控制工具相比起写代码是一项比较不受重视的技能,在这种情况下翻历史是非常耗时的。当然,有些人会将一些细节以注释的形式添加到代码中,但注释也只能承载很小的一部分信息,因为维护注释也是一项很高的成本,我个人一向是反对添加注释来解释代码的。

所以阅读代码实际上并没有看上去那么轻松,为了彻底理解一段代码,很有可能你需要付出和编写这段代码差不多的努力,来了解这段代码的历史和前提。

前面提到的是阅读「好的代码」的情况,比如大多数活跃的开源项目,如果是面对质量较差的代码情况就更为糟糕了。

所以我的观点是,读代码绝对不是一种好的学习方式,我认为学习一项技术应当先阅读书籍,然后尝试自己实践,最后再参考代码质量较高开源项目。

对于大多数项目而言,可能从未把「供他人学习」当作目标,只有当你自己实践过,积累了一些经验并且也遇到过一些困难的时候,你才能读懂代码并且从中学到解决问题的技巧。而在开始实践之前,最好的知识来源是书籍,因为书籍的内容是经过精心的安排的,最高目标就是供他人阅读。

精子生于 1995 年,英文 ID jysperm.

订阅推送

通过 Telegram Channel 订阅我的博客日志、产品和项目的动态:

王子亭的博客 @ Telegram


通过邮件订阅订阅我的博客日志、产品和项目的动态(历史邮件):

该博客使用基于  Hexo  的  simpleblock  主题。博客内容使用  CC BY-NC-ND  授权发布。最后生成于 2025-02-10.