Calcit 相比 Clojure 一些有意思的元编程能力 #226
tiye
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Cirru Edn 里写代码
Calcit 使用的数据格式 Cirru EDN, 本身用了 Cirru 的语法做标记,
于是代码的语法是基于 Cirru 的, 数据的格式也是基于 Cirru 的,
结果, 我可以直接把 Calcit 代码通过一个
quote
的指令直接放在数据当中, 写法比如:其中
%{}
是一个类似 HashMap 的结构, 其中包含了一段代码.这段代码来自 Calcit 的源码, 可以把程序的定义都放在这样的数据结构当中:
https://github.com/calcit-lang/calcit/blob/main/src/cirru/calcit-core.cirru#L1108-L1113
这为 Calcit 提供了不小的便利, 比如 Calcit 的 API 文档, 完全就是这样写的:
https://github.com/calcit-lang/calcit-apis/blob/main/docs/apis.cirru#L8-L10
格式化表达式
Calcit 的宏大致模仿的 Clojure, 相对较弱, 不过因为用的 Cirru 语法, 格式化非常方便,
比如这个例子当中我要查看代码结构, 可以通过
format-to-cirru
输出带缩进的格式化结果:模拟 Sum Types
由于在动态语言里缺少类似 Rust enum 类似结构来模拟 sum types, 我在 Calcit 尝试类似的方式模拟,
比如用 List 来模拟, 然后第一个元素标记作为 tag 的方式来用, 然后配合 pattern matching 语法,
后来再基于之前从 Rust 模拟的 Tuple 结构再做简化, 形成了现在的语法:
其中
::
用于定义 Tuple 结构,:complex
是一个 Tag, 对应 Clojure 里的 keyword.同时这个结构也提供一部分面向对象的写法的能力,
其中
%::
相比::
可以额外在设置一个 prototype 的结构, 这里不展开,但这样设定以后,
.add data
可以用类似面向对象的方式调用.(尽管整体是不可变数据, 也不会有继承)当然这个功能来说, Clojure 是有很多多态手段的, 整体是更强大的.
展开符号内部信息
&extract-code-into-edn
将一个 symbol 一些内部信息展开, 比如得到 symbol 得命名空间和所在函数定义的信息.获取命名空间在 Clojure 当中应该是有的, 而获取所在定义应该是 Calcit 独有(来自 Cirru 结构设计),
这个功能本身不算复杂, 配合 Macro 能提供比较有意思的功能, 比如 Respo 定义的 CSS in Calcit 写法
实际使用时就可以通过 defmacro 获取到
style-a
的命名空间(比如说app.main
), 和符号内容,于是就可以生成一个应用级别的
style-a__app-main
这样一个字符串, 作为className
使用.这个对应 emotion css 的场景, emotion 会生成
css-1223d3
这样带着 HASH 的名字,要进一步加上 Babel 编译, 能带上原始的命名. 这个在支持 Macro 的语言当中就很容易了.
再配合定义的位置, 就可以定义出来一个全局唯一的名字, 比如
<namespace>_<def>_<local>
这样,这个字符串可以作为一个全局的 key 使用, 关联到一个全局状态上.. 从而模拟一个局部状态.
之所以我觉得这样有意思, 是因为这个场景有点类似
useState
这类局部状态的能力,(尽管看看效果, 还是感觉不够简短, 但也算提供了一种并不繁琐的思路)
数据转回代码
&data-to-code
有意思的地方是把数据转回代码:这个是方便生成 JavaScript 用的. 实际当中场景是从文本的配置中读数据, 生成 JavaScript 代码再重新执行得到数据.
这个可以提供一些便利, 把配置数据从 Calcit 程序当中分离出来, 同时避免再浏览器端重新做文本解析.
Beta Was this translation helpful? Give feedback.
All reactions