+++ title = "稻草人周刊 Vol.82" subtitle = "Save all romanticism for my inner life." tags = ["AI","消费主义","软件","Lisp","Web"] date = 2026-05-25 09:36:43 draft = false toc = true +++ 这周我把堆积在书签里没读的文章翻出来读了,发现其中有不少已经是两三个月前发布的内容了,他们跟随着一次次书签管理器迁移漂泊了好久,终于在周刊里找到了归宿。 --- ## 止语 {{< music cover="https://img.eltr.ac/geedeapro/weekly/82/the-tortured-poets-department.jpg" title="I Hate It Here" artist="Taylor Swift" >}} > I'm lonely, but I'm good > > I'm bitter, but I swear I'm fine > > I'll save all my romanticism for my inner life and I'll get lost on purpose > > This place made me feel worthless > > Lucid dreams like electricity, the current flies through me > > And in my fantasies, I rise above it > > And way up there, I actually love it --- ## 连接 ### 我抛弃孩子,爱上妈妈,被全欧洲封杀,但我是个好人 > [!note] 📻 > [《独树不成林》我抛弃孩子,爱上妈妈,被全欧洲封杀,但我是个好人](https://www.xiaoyuzhoufm.com/episode/6a08e1b6e1eb34a9398856a6) 八卦不仅仅是娱乐方式,甚至是刻在人类基因里的生存本能,原始部落里有关哪个地方有猎物、哪里有野兽的信息就是通过类似八卦的方式口口相传的。得到更多的信息就意味着更高的生存几率。这么说来,八卦不仅不坏,甚至是有益于生存的、顺应天性的活动啊。 我跑题了。总之,偶尔读一读已故名人的八卦很有趣。 卢梭的《忏悔录》非常暴露地描写了自己人格中各种阴暗的部分,比如在贵族小姐面前露鸟、在见人之前为了不性欲大发所以在林子里自慰、把自己的第一次性行为献给了养母(以及他真的爱过她)…… 他和一些同时期的名人的关系也很有意思,会因为各种事情跟朋友写绝交信,临死前他的床榻旁已经没有什么人了,因为朋友都被他自己推走了。 我感觉,我在听一个一生都没有经历完整社会化的人的故事,想必童年失去父母的经历对他人格的影响真的很大,而他自己显然没有解决这些问题的意识(我严重怀疑卢梭甚至引以为豪,因为他在大庭广众之下数次朗读自己的《忏悔录》……)。不过,无可指摘的是,他的确创作了很多伟大的作品,而且从不生产烂作。 所以,我自己的童年也不算幸福(尽管也远不如他的悲惨),希望我不要变成卢梭那样的人。 ### 看好你的硬件 > [!note] 📜 > [Hold on to Your Hardware](https://xn--gckvb8fzb.com/hold-on-to-your-hardware/) “AI”的算力需求越来越多,包括 RAM、SSD 和芯片在内的各种硬件资源全都流向了企业建造的数据中心。由于供不应求,硬件的价格越来越高,作者预言这次短缺并不是临时的,不像以往的加密货币挖矿潮和其他的临时短缺,如今的硬件价格高昂可能是常态,所以要做好没办法随时用可支付的价格更换硬件的准备。 对硬件制造商来说,企业端的需求更大,给的钱也更多,而消费者端不仅给的钱少,*要求也多,不好满足*,如此看来,制造商优先满足企业需求是必然的,给消费者的硬件产品只会越来越少,甚至越来越贵。作者还预言,既然已经有企业提供租用电脑服务了,如果这种趋势一直发展下去,那么我们可能会迎来大部分人都不拥硬件、不拥有算力的时代,大家都只持有一台连接到云计算机的终端,缴纳订阅费使用此类服务。联想到已有的各种 SaaS 和流媒体服务,硬件即服务的未来似乎也不那么像是杞人忧天了。 这也导致算力和权力结构的中心化。当然,如果依照 [Volpe 的预言](https://martinvol.pe/blog/2026/03/30/how-the-ai-bubble-bursts/),“AI”泡沫必将破裂的话,“AI”公司建立的数据中心终将闲置,人们将有办法购买或租赁到非常便宜的云服务器。 就算不相信这样的未来即将到来,「Buy it for life」的思想也是值得学习的。即便消费者文化持续,也不代表所有人都要参与到这种文化当中。 ### 框架的假象 > [!note] 📜 > [The framework mirage](https://numergent.com/2016-06/The-framework-mirage.html) *所以 Planet Clojure 为什么会把一篇十年前的文章推送到 Feed 里?* 这篇文章先描述了这种困境:如果你使用框架编写 Web 应用,一开始可能会感到非常顺手,CRUD 以及各种基本操作非常省心,但投入很多时间精力过后,可能会发现框架的部分功能并不称心如意,这个时候提交问题可能并不会得到结果(比如开发者认为这不在框架的问题范围之内),这个时候你的选择要么是抛弃这个框架重写,要么是 Fork 这个框架自己添加想要的功能,但都很耗时耗力。 然后作者介绍了 [Luminus](https://luminusweb.com/),一个轻量级的 Clojure Web 框架。这个框架的特点是,它需要你自己做很多决策,比如用什么数据库,引入什么第三方库,此外,脚手架一旦搭好,Luminus 的未来更新完全不会影响你的代码,除非你主动选择。 看起来不错,但我想,使用任何一种框架,最核心的业务逻辑都应该是高度抽象的,不应该依赖底层细节,而框架提供的 CRUD 等能力明显是底层细节。如果开发者被框架困住,我想他需要审视一下自己的架构决策,让业务逻辑直接依赖框架是非常糟糕的。迁移框架当然要费不少精力,但绝非「必须重写」那么巨大。 举个例子:  {.dark:invert} 上述箭头描述的是依赖关系,空心箭头表示实现接口。简单来说,有一个中间人(Middle Man)了解如何调用框架和包含业务逻辑的服务类,这样业务逻辑就不需要直接依赖框架了。如果后续需要更换框架,只需要重写中间人,业务逻辑无需修改。 同时要注意 `Service` 是高度抽象的,不依赖任何其他组件,如果需要调用别的具体实现,应该写在 `ServiceImpl` 实现类里,这样就保证了业务逻辑足够抽象,不依赖不应该依赖的东西(接口隔离原则),也保证业务逻辑不需要经常修改,有需求变动时只需要拓展或更换实现类(开闭原则)。 ### 用 Lua 写 HTML 模板可能很酷 > [!note] 📜 > [Lua can be a really cool HTML templating engine](https://riki.house/lua-html) 作者先对比了 [Handlebars](https://handlebarsjs.com/) 和 Go 的 [html/template](https://pkg.go.dev/text/template) 库,两者是相似的 HTML 模板引擎,都允许用户在 HTML 模板里用类似 `{{ }}` 的语法插值,编写简单的赋值语句、循环结构和条件结构等等。不过 Go 的库要比前者(Rust 实现)更小巧,功能也差不多,在某些设计上也更讨喜(比如 Handlebars 有可怖的三括号语法 `{{{ }}}`,~~Lisp 程序员看了都摇头~~)。 后来作者发现了 [Maud](https://maud.lambda.xyz/)(Rust 的宏),可以用这种格式写 HTML: ```rust html! { h1 { "Hello, world!" } p.intro { "This is an example of the " a href="https://github.com/lambda-fairy/maud" { "Maud" } " template language." } } ``` 看起来简洁得多,但实现很复杂,这个宏有 3000 行左右的代码,作者不喜欢。而 Lua 这边能使用十分简洁的原生语法,实现类似的效果,这归功于 Lua 优雅的语法设计,在函数只有一个参数时可以省略括号(比如 `print("a")` 可以写成 `print "a"`),如果这一个参数是数组或映射(Lua 的 `table`),就可以在不写圆括号的情况下,用花括号写入数组或映射的值。 如果用 Lua 的函数作为 HTML 标签的表示,就可以得到如下的结构。 ```lua h.Document{ lang = "en", h.head{ h.meta{charset = "UTF-8"}, h.title{"Hello, world!"}, }, h.body{ h.h1{"Hello, world!"}, h.p{ "This is an example of the little HTML templating framework I wrote ", "in Lua in like 20 minutes." }, h.p{ "As you can see, it is fully capable of generating any kind of markup ", "you'd ever want.", h.br(), "It can even do things like ", h.b"bold text", "!" }, h.p"This is some embedded HTML
" h.img{ alt = "riki sitting in pink space", src = "https://riki.house/static/character/riki/sitting.png", width = 2223, height = 1796, style = "width: 20%; height: auto;" }, }, } ``` 另一个好处在于,由于是直接用一门编程语言写的模板,只要利用语言本身的变量、字符串拼接、循环和分支结构,就不需要实现类似 `{{ }}` 的语法了。据我所知,Lua 本身也十分小巧,很容易嵌入其他语言编写的程序当中,就算不用 Lua 写博客程序,也可以专门嵌入在程序里做模板引擎。 好了,写到这里我终于憋不住了:*Lisp can be the coolest HTML templating engine!* ```clojure [:html [:head [:title "Eltrac"] [:meta {:charset "utf-8"}] [:meta {:name "viewport" :content "width=device-width,initial-scale=1"}]] [:body [:main.i-am-a-class#id [:p "Be cool. Use Clojure."]]]] ``` 上面是 Clojure 的 [Hiccup](https://github.com/weavejester/hiccup) 库,不少其他的 Lisp 方言也有类似的库,因为用 S-expression 编写 HTML 非常自然且简洁。[Spineret](https://github.com/ruricolist/spinneret)(Common Lisp 库)的写法与 Hiccup 类似,但只有圆括号: ```lisp (defmacro with-page ((&key title) &body body) `(with-html (:doctype) (:html (:head (:title ,title)) (:body ,@body)))) (defun shopping-list () (with-page (:title "Home page") (:header (:h1 "Home page")) (:section ("~A, here is *your* shopping list: " *user-name*) (:ol (dolist (item *shopping-list*) (:li (1+ (random 10)) item)))) (:footer ("Last login: ~A" *last-login*)))) ``` 你看到那几个优雅得要命的宏了吗? ### Lisp 设计模式 > [!note] 📜 > [Lisp Design Patterns](https://aartaka.me/lisp-design-patterns.html) 可能是我对设计模式这个概念的理解有偏差(也可能是因为软件领域的用词向来很混乱……),我总觉得这篇文章更像是在介绍如何利用语言特性,而不是在讲设计模式,或者仅仅是编程模式(Programming Patterns)而非设计模式(Design Patterns)。 首先是允许后端成为软件的插件(也就是随时更换具体实现)的 Protocols(协议)和 Monkey Patching(猴子补丁)。Common Lisp 和 Clojure 都支持 Protocol(`defgeneric` 和 `defprotocol`),这和 OOP 语言的 Interface 差不多,可以把面向接口编程的经验复用到一些 Lisp 中。至于 Monkey Patching,是指一些语言允许用户改写已有的函数,比如 Emacs Lisp 的 Adivces 和 Common Lisp 的 `:around` 方法,也就允许用户自己编写并替换软件的部分实现。 然后是 Hooks(钩子),这就更接近设计模式了,把函数存放在一个列表(或别的什么数据结构里),当有事件发生时,就调用这里面的函数。我想起某本 Java 设计模式里也有类似的案例,叫作观察者模式(Oberver Pattern)或监听者模式,被监听对象储存一系列监听者的引用,当事件发生时,由监听对象调用监听者的方法。不过在函数式编程中,我们直接储存函数本身,不和对象打交道。 其次是 Fail Fast, Fail Often(快错常错),这更像是开发模式。简而言之,作者表示他在非 Lisp 语言里很害怕看到报错,但在 Lisp 里就不会,因为以 Common Lisp 为首的部分 Lisp 方言有类似的语言特性,可以快速在报错之后快速重启程序,如果遇到暂时不想处理的错误,可以直接忽略,还能使用 `use-value` 这样的特性临时替换有问题的参数。在 Common Lisp 以外的方言,程序员也可以使用 REPL(可以理解为直接与代码库交互的命令行程序)调试程序,在 REPL 里也可以重新定义函数和变量,重新运行程序,非常方便。 再者是 Dynamic Variables(动态变量),看起来很吓人的一个模式,因为它似乎违背了函数式编程范式,给变量赋值了。这是作者给的示例代码: ```lisp (defmethod gimage:apropos* :around (string &optional package external-only docs-too) (let ((*print-case* :downcase) (*print-level* 2) (*print-lines* 2) (*print-length* 10)) (call-next-method))) ``` 我不懂 Common Lisp,但我熟悉 `let`,这个函数(也可能是宏或者保留字,我不清楚底层是怎么实现的)的第一个参数是是个储存二元关系[^1]的列表,用于声明绑定(Bindings),余下的参数都是待求值的表达式。这里确实是给变量赋值了,但仅限于 `let` 块内,是在限定作用域内改变状态以修改行为的做法。无论怎么说,我还是觉得这不太优雅,为什么一定要用绑定,不能给函数传递参数呢? 最后是 Destructuring(解构),这其实是语言特性,它的作用是把复杂的数据结构分解为扁平的、易用的结构,比如把键值对映射解构为几个简单的变量。作者给的 Guile/Scheme 例子有太多我不熟悉的函数,所以我写个 Clojure 的例子。 ```clojure (defn http-request [url & {:keys [Accpet Cookie Content-Type] :or {} ;; 默认值 :as opts}] ;; 直接使用 Accept、Cookie 和 Content-Type ;; 也可以用 opts,结构是 {:Accept ... :Cookie ... :Content-Type ...} ) (http-request "https://example.com" :Cookie {"key" "value"}) ``` 这在构造有很多可选参数的函数时非常有用。如果用户传入的参数本身就是个映射,则可以用 `let` 解构。 ```clojure (defn http-response [response] (let [{:keys [status headers body]} response] ;; 直接使用 response map 里的 status、headers 和 body )) ``` 不过这也不是 Lisp 专属的,因为 JavaScript 也可以解构。 ```js const { status, headers, body } = response [a, b, ...rest] = [10, 20, 30, 40, 50]; console.log(rest); // Expected output: Array [30, 40, 50] ``` 印象中 Python 也可以,作者甚至把解构称作「Pythonique」。 --- ## 星群 ### Jant Owen 开发的博客程序,兼具动态博客的便利和静态博客在长期保存方面的优势,很有趣的设计。 > 所以其实我的痛点是我想既要又要,既想发布方便,又想保有某种长期格式。我给 Jant 设计了自动同步的 GitHub Sync 解决了这个问题。Jant 博客可以生成一个完整的 Hugo 站点,每次在 Jant 发帖都会自动同步到 Github Repo,这里是一个示例 Repo。它既是备份,也是一份完整的,可以独立运行的静态博客。 不过要是能用 Git 同步到任意远程仓库就好了,实在不想用 GitHub。 访问:[Jant](https://www.owenyoung.com/jant) --- ## 切片 - 自我暴露不只会引来有相同感受的人的亲近感,产生有意义的交流,还有可能引来平庸者站着说话不腰疼的评价,说的是我听过也想过而且和我想表达的东西毫不相关的陈词滥调,想半天还是不理对方比较好。 - 某天和朋友玩星露谷物语,一边钓鱼一边闲聊,聊起《存在主义咖啡馆》这本书。我半开玩笑说加缪就是那本书里讨论的哲学家里最帅的一个,说着就从我书房里找到了四张加缪的照片,然后…… 然后我就意识到自己好像有点变态,不敢去找那第五和第六张了。 但是加缪真的太帅了,~~法兰西顶流帅哥~~。 朋友:希望你只是把他当作你的精神导师。 [^1]: 死去的离散数学突然开始攻击我…… 这里说的二元关系就是只储存了两个元素的列表,实际上是键值对。