6.4 KiB
核心语法
每个片段都链接到随 skill 提供、经过检查的完整源码。先看它所属的角色和调用链,再复用到项目;不要把片段单独当作完整 HTTP 项目。
类型和函数
源码:模板 App
greet(name: Text) (message: Text) {
message = "Hello, " + name
}
函数写成 name(输入) (命名输出) { 语句 },不写 function/return,调用按位置传参。没有输入或输出时也要保留 ()。一个输出直接得到值;多个输出按名字访问。
App 的参数、返回值和向外传播的错误都属于公开接口,不能暴露 Domain/Adapter 私有类型。
函数命名表达业务行为,调用路径不附加主题文件名。相关函数、类型和同名分句可以同处一个内聚文件;拆分与复用的判断见 文件与函数组织,不按函数长度自动增加 helper。
常用类型:Bool、Int(检查溢出)、Float、精确 Decimal、Text、Bytes、Uuid、Id、DateTime、Date、Time、Duration、Secret、Json。可空用 T?,集合用 List<T>、Map<K,V>。Model ID 是每个 Model 独有的名义类型,不等于任意 Int。不得把不同表的 ID 混用。
record 用字段初始化;choice 用分支构造。以下 State、label、display 和 grade 位于同一份语法测试,测试入口覆盖每个分句;实际业务无需创建这些演示函数。
源码:分句测试
type State {
Draft
Published
Rejected(reason: Text)
}
源码:分句测试
label(state: State.Published) (text: Text) {
text = "published"
}
label(state: State.Rejected(reason)) (text: Text) {
text = reason
}
label(state: other) (text: Text) {
text = "draft"
}
分支与边界
用同名函数的不同分句表达条件,不写 if/else/switch/match、条件表达式或递归。这些分句要连续,参数数量和输出声明一致。
每种输入必须恰好匹配一个分句:不能漏掉,也不能重叠;写在前面不会优先匹配。other 表示该参数位置没有被其他明确模式覆盖的值。
源码:分句测试
display(value: Text) (text: Text) {
text = value
}
display(value: null) (text: Text) {
text = "missing"
}
grade(score: Int < 60) (text: Text) {
text = "fail"
}
grade(score: Int >= 60) (text: Text) {
text = "pass"
}
支持完整类型、choice 分支、null、Bool/Text/数值字面量、Int/Decimal 范围与 other 模式;多输入检查全部输入组合。任一函数都必须为命名输出赋值。不要写 return 提前退出,按分句表示结果与失败。
字面量分句适合明确的状态和规则,不意味着可以按某个用户 ID、租户名字或样例数据写业务特例。运行时配置与可信身份的归属见 配置与硬编码。
集合与处理函数
Dever 用 each 同时表达映射和遍历,没有独立的 map:处理函数有一个输出时生成 List,没有输出时逐项执行动作。filter 按 Bool 输出筛选,reduce 按初始值逐项累积。它们是语言核心操作,不需要导入集合库。
下面的 App 选出已发布且标题非空的文章,整理标题并统计总字数。输入、结果类型与完整调用链都在 集合项目 内。
源码:文章 App
type Article {
title: Text
published: Bool
}
type Summary {
titles: List<Text>
title_length: Int
}
summarize(articles: List<Article>) (summary: Summary) {
published = filter(domain.is_published, articles)
titles = each(domain.published_title, published)
summary = Summary {
titles = titles
title_length = reduce(domain.add_title_length, titles, 0)
}
}
处理函数直接使用有业务含义的命名函数。参数顺序、类型和输出由编译器检查,不使用 lambda 或运行期闭包:
源码:文章 Domain
is_published(article: app.Article) (selected: Bool) pure {
selected = article.published and text.trim(article.title) != ""
}
published_title(article: app.Article) (title: Text) pure {
title = text.trim(article.title)
}
add_title_length(title: Text, total: Int) (next: Int) pure {
next = total + length(title)
}
filter 的处理函数接收一个元素,输出 Bool;each 的单输出成为新 List 的元素;reduce 按“元素、当前累计值”传参,输出下一个累计值。顺序保持不变,空集合的 reduce 返回初始值。本例初始值为 0。
集合项目的 summary 测试 用 each(assert_clean_title, actual.titles) 逐条断言。这个处理函数没有输出,所以该调用没有返回 List。测试还验证筛选顺序、清理结果、总字数和空集合。
失败和能力
业务错误通过 choice 的 error 分支声明,fail 传播,result(call) 显式捕获;普通调用直接得到成功值。不能用字符串/任意 JSON 代替类型化失败,不能捕获后悄悄吞掉错误。错误映射到 HTTP 的方式见 application.md。
Domain 的纯计算函数可以声明 pure,它调用的方法也要满足纯计算要求。Model 的 REST 字段转换只能调用合规的同领域 pure Domain。文件、网络等外部操作经 Port/Adapter,不直接放进普通 App 或 Domain。
不需要写 async/await,普通调用依次完成。要并发执行时用 run 启动子任务,再用 wait/stop 收尾;不能把任务留到当前作用域之外。
Group/Channel/Stream 的使用范围由编译器检查,不能跨等待、线程或请求随意保存句柄。result 只负责捕获结果,不改变调度,也不让原本禁止的操作变成合法。
Secret 不可打印、比较或转为普通 Text。密码使用现有 crypto 能力,不自制哈希。类型、分句或调用能力报错时修正真实问题,不增加中转函数、通用包装或吞错分句来绕过检查。
更多完整语法以官方核心仓库同版本 LANGUAGE.md 为准;新建项目先使用本 skill 的已验证模板。