Skip to content

필사 모드: 在 Emacs 中,一切看起来都像服务:用客户端-服务器视角重读"编辑器即操作系统"这个梗

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

引言 —— "Emacs 是操作系统"这一误读

Charles Choi 的文章《In Emacs, Everything Looks Like a Service》最近在 Hacker News 上引发了热议。有意思的是,这篇文章从"Emacs 是操作系统(OS)"这个常见的赞誉出发,却随即否定了它。作者的说法很明确:这个比喻并不成立,真正让人联想到 OS 的,是 Emacs 在内核之上编排(orchestrate)应用程序与各种工具的能力。

他给出的替代图景更为准确。因为 Emacs 能够直接访问文件系统、网络这类 OS 服务,并且能运行其他程序,所以在它里面即兴地做一个客户端来用,就成了家常便饭。这正是"只活在 Emacs 里(living only in Emacs)"这种说法之所以可信的原因。结论就如标题所言:在 Emacs 中,一切看起来都像服务。

这里的"服务"不是流行语,而是一个精确的技术术语。这篇文章真正的主张,并不是说 Emacs 是一台运行守护进程的服务器,而是说 Emacs 是一个通用客户端。这个区分正是全文的核心,也是一个比陈旧的"OS 梗"有用得多的视角。

客户端-服务器这个视角

文章的骨架是一个教科书式的客户端-服务器模型。它把任务分成资源的提供者(服务)和请求者(客户端),客户端发出请求,服务器返回响应。这种交易可以跨越网络,也可以在系统内部局部地发生。作者所倚重的,正是这个"也可以在本地发生"。

他把客户端大致要负责的事情归纳为三件:UI、与服务通信的客户端边缘(client edge),以及存放往来数据的本地数据库。而在 Emacs 里,针对这三件事各自的内置库都已经现成。UI 方面有迷你缓冲区(minibuffer)、缓冲区、自动补全、tabulated-list-modevtabletransient;客户端边缘有 url、TCP/UDP 套接字、SMTP、JSON/XML 序列化;本地数据有关联列表(alist)、属性列表(plist)、哈希表、SQLite。把这些粘合在一起的胶水是 Elisp —— 一门能在运行时即兴组装行为的动态语言。

作者举的例子是 wttr.in 这个面向控制台的天气服务。他做了一个 wttr 命令:接收一个地点,发出 HTTP 请求,解析 JSON 响应,再把结果显示在迷你缓冲区里。关键在于代码量。完成后的 wttr.el,按 cloc 统计是 67 行。更有意思的是第二个版本。如果把网络请求和 JSON 处理交给一个叫 weather 的 Python 脚本,那么 Elisp 这一侧要做的就只是调用一下 shell 而已。

(defun weather (location)
  "Call weather script with LOCATION and show result in minibuffer."
  (interactive "sWhere (default: local): ")

  (let* ((weather-cmd "weather")
         (cmd (if location (format "%s %s" weather-cmd location) weather-cmd))
         (result (shell-command-to-string cmd)))
    (kill-new result)
    (message result)))

正如作者所说,这么一来,shell 命令本身实际上就成了一个可以向其发送请求的服务。既然现成的命令行工具能替你完成繁重的活儿,那就可以把这个工具重新定义为一个通过 shell 调用来访问的"服务"。这一句话,撑起了整篇文章。

为什么这是一种独特的计算模型

从这里开始是我的分析。我们平时用的模型是"一个任务一个应用":网页用浏览器,邮件用邮件客户端,shell 用终端。每个应用都自成一个世界,要在它们之间往返,就得忍受复制粘贴和窗口切换的代价。

Emacs 提出的模型正好相反。在一个长时间运行、始终开着的 Lisp 环境里,每一项能力都被实现为面向某个服务的一个薄客户端。无论是天气、邮件、git 还是数据库,全都跑在同样的缓冲区、同样的键位映射、同样的 Elisp 之上。作者说的"只活在 Emacs 里"之所以成立,原因就在这里。你不再在应用之间来回穿梭,而是随手拼出一个向服务发送请求的客户端,贴上去就用。

准确地说,这篇文章的主张并不是缓冲区或进程本身就是服务。服务在外部 —— 要么是网络 API,要么是 shell 背后的 CLI 工具。Emacs 所做的,是成为面向这些服务的客户端。所以这个视角比"Emacs 是操作系统"这个梗更准确。Emacs 并不取代内核,它只是成为一个向内核之上的各种资源搭话的通用窗口。

魅力与代价

魅力是实打实的,并不夸张。一个天气客户端 67 行,包裹 CLI 的版本只有 9 行。Unix 上存在的大量命令行工具,都直接变成了可以调用的服务。统一性也很有分量:一旦学会的编辑、搜索、键位映射,会原封不动地适用于你做的每一个"客户端"。可组合性与即兴性 —— 这才是这个模型真正的卖点所在。

代价也要同样诚实地看待。第一,作者自己的代码就暴露了要付出的代价。fetch-json-as-hash-table 用的是 url-retrieve-synchronously —— 一个同步调用。Elisp 实际上是单线程的,所以在请求往返的这段时间里,整个编辑器都可能卡住。第二,抽象层次很高,但底层的管道活儿得你自己攥着。你要越过 HTTP 头,把 point(光标位置)移动到 url-http-end-of-headers,还要为了防止内存泄漏,亲手对网络缓冲区执行 kill-buffer。第三,更根本地说,这样做出来的客户端,大多只是对专用应用早已做得更好的事情的一次薄薄的再实现。这是一桩用完成度去换取集成度与可组合性的交易。

所以,"Emacs 是个很棒的操作系统,只可惜缺了一个能用的编辑器"这个老笑话所留下的张力,至今依然成立。只不过这篇文章绕开了这个笑话。如果不把 Emacs 叫作操作系统,而是叫作通用客户端,那么"什么都能做"的魅力,和"却没有哪一样做到最好"的代价,就能在同一口气里一并说清。这个模型对已经生活在 Emacs 里的人来说非常强大。而对不是这样的人来说,亲手拼装薄客户端的乐趣,本身也正是代价。

结语

这篇文章真正的贡献不是什么新功能,而是一次精准的框定。它把"Emacs 是操作系统"这个松散的梗,换成了"在 Emacs 中,一切看起来都像服务"这样一个可验证的主张。而这个主张是有代码支撑的 —— 67 行的天气客户端,9 行的 shell 包装器。

我想补充的是一种平衡。这个视角在解释魅力的同时,也同样解释了代价:同步调用带来的卡顿、必须自己攥着的底层管道、相比专用应用更低的完成度。正如作者结尾那句话所说,这种能力,对能看出其中机会的用户来说是强大的。我要在后面再添一行 —— 在你选择这个机会的那一刻,你也一并选择了完成度与可组合性之间那桩由来已久的交易。

参考资料

현재 단락 (1/26)

Charles Choi 的文章《In Emacs, Everything Looks Like a Service》最近在 Hacker News 上引发了热议。有意思的是,这篇文章从"Emacs ...

작성 글자: 0원문 글자: 3,096작성 단락: 0/26