diff options
| author | eltrac <hi@eltr.ac> | 2026-05-21 11:18:24 +0800 |
|---|---|---|
| committer | eltrac <hi@eltr.ac> | 2026-05-21 11:18:24 +0800 |
| commit | 28d1a83d54bce3e73675267891dd067fbe38cd93 (patch) | |
| tree | 037f70da7f4bb79fe03fc34052c36d59bc22349d /content | |
| parent | a28464fc11b092db1449bfd3da464ba13caa0e93 (diff) | |
[draft] update weekly draft
Diffstat (limited to 'content')
| -rw-r--r-- | content/weekly/82.md | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/content/weekly/82.md b/content/weekly/82.md index ed3e8707..78ff1f2e 100644 --- a/content/weekly/82.md +++ b/content/weekly/82.md @@ -15,6 +15,11 @@ toc = true ## 连接 +### + +> [!note] 📻 +> [《独树不成林》我抛弃孩子,爱上妈妈,被全欧洲封杀,但我是个好人](https://www.xiaoyuzhoufm.com/episode/6a08e1b6e1eb34a9398856a6) + ### 看好你的硬件 > [!note] 📜 @@ -28,6 +33,122 @@ toc = true 就算不相信这样的未来即将到来,「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 <p></p>" + 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] 📜 |
