关于 elixir-meta-programming
面向 Elixir 开发者的宏与声明式 DSL 编写指南,适用于函数无法满足语法或编译期需求的场景。内容聚焦 quote 卫生、DSL 代码生成和编译期验证。
主要功能
- 01宏设计与运行时委托
- 02quote 求值与卫生
- 03声明式 DSL 构建
- 04编译期验证与错误定位
适用场景
- 01实现 Elixir 宏
- 02根据声明生成模块代码
- 03检查宏的求值与卫生问题
- 04将 DSL 错误定位到调用源码
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ pproenca/ dot-skills --skill elixir-meta-programming安装后在 Claude Code 中输入/elixir-meta-programming确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
AGENTS.mdmetadata.jsonassets/templates/_template.mdreferences/_sections.mddsl-accumulate-attributes.mddsl-introspection-function.mddsl-unquote-fragments.mddsl-using-minimal.mdmacro-defguard-for-guards.mdmacro-thin-delegate-runtime.mdquote-bind-quoted-single-eval.mdquote-escape-runtime-data.mdquote-rely-on-hygiene.mdvalid-fail-at-compile-time.mdvalid-locate-errors-at-caller.md详细介绍
Elixir 元编程与 DSL
如何正确构建 Elixir 宏和声明式 DSL——适用于确实需要宏、而普通函数无法胜任的情况。每条规则都针对一种常见的错误默认做法:能力出色的模型一开始编写 quote/unquote、__using__ 和 @before_compile 时就容易犯这种错误;这里不会重述模型已经掌握的宏基础知识。
首先:真的需要宏吗?
默认答案是不需要——先考虑使用高阶函数、行为,或“数据 + 解释器”。是否该使用宏不属于本技能的职责;相关内容在关联技能中,打开本技能前应先应用它们:
staff-level-elixir→data-functions-over-macros(优先用函数解决问题)adversarial-elixir→meta-functions-not-macro-dsl、meta-use-is-not-import、meta-no-compile-time-coupling
只有当你需要函数无法提供的能力时,宏才有用武之地——新语法、控制参数是否及何时求值,或必须在编译时存在的代码(例如编译 schema,或根据声明列表生成子句)。一旦达到这个门槛,本技能就会教你如何正确实现。
适用场景
- 编写宏——确定哪些内容放进
quote块,哪些交给运行时函数处理,以及参数如何求值 - 构建声明式 DSL——通过
schema/spec/route/workflow块收集声明并生成模块 - 配置
use MyDSL——确定__using__应注入什么,以及哪些内容应放在@before_compile中 - 在编译时根据声明列表生成函数或子句
- 让 DSL 在
mix compile时失败,并提供指向用户源码的错误信息 - 审查现有宏/DSL 代码中的求值、卫生性、转义和编译时验证问题
规则类别
| # | 类别 | 前缀 | 涵盖内容 |
|---|---|---|---|
| 1 | 宏设计 | macro | 单个宏的结构——基于运行时函数的轻量宏;用于 guard 安全结构的 defguard |
| 2 | Quote 与卫生性 | quote | 编译时语义陷阱——多次求值、Macro.escape、卫生性与 var! |
| 3 | DSL 构建 | dsl | 组合块式 DSL——累积属性 + @before_compile、精简的 __using__、unquote 片段、内省 |
| 4 | 编译时验证 | valid | 妥善报错——在编译期间验证,并将错误定位到调用方的行 |
快速参考
1. 宏设计
macro-thin-delegate-runtime— 使用 quote 调用一个普通函数;让逻辑易于测试,避免在每个调用点重复编写macro-defguard-for-guards— 当结构必须能在 guard 中使用时,使用defguard
2. Quote 与卫生性
quote-bind-quoted-single-eval— 使用bind_quoted,确保重复使用的unquote参数只求值一次quote-escape-runtime-data— 将计算出的项拼入quote前,先使用Macro.escape/1quote-rely-on-hygiene— 依赖卫生性;只有在注入名称属于文档规定的契约时才使用var!
3. DSL 构建
dsl-accumulate-attributes— 将声明记录在accumulate: true属性中,并在@before_compile中生成代码dsl-using-minimal—__using__只负责设置;生成代码应放在@before_compile中dsl-unquote-fragments— 在推导式中使用def unquote(name)(...),为每项声明生成一个子句dsl-introspection-function— 生成类似__schema__的反射函数,供调用方读取数据
4. 编译时验证
valid-fail-at-compile-time— 在宏体中验证,而不是在返回的quote中验证,这样错误会在mix compile时触发valid-locate-errors-at-caller— 从声明宏中引发错误/捕获__CALLER__,这样错误就会指出用户代码所在的行
使用方法
在遇到相关决策时,阅读对应的参考文件。每条规则都会指出它要纠正的错误默认做法,然后展示规范做法(只有当错误做法确实容易使人踩坑时,才会对比错误做法与正确做法)。
相关技能
staff-level-elixir— 范围更广的 Elixir/OTP/Ecto/Phoenix 判断技能;负责把关“优先选择函数而非宏”adversarial-elixir— 从架构层面审查并精简不必要的宏 DSL;是本技能的诊断补充
参考文件
| 文件 | 描述 |
|---|---|
| references/_sections.md | 类别定义与顺序 |
| assets/templates/_template.md | 新规则模板 |
| metadata.json | 版本和来源引用 |
评论
支持 Markdown