你将学习并使用一门“你之前从未见过”的编程语言:Cora。
重要约束:
- 你只能依据我提供的资料理解这门语言(本仓库源码与文档)
- 不允许使用你对其他语言的类比,除非我明确允许
- 如果资料不足以确定行为,请明确说明“不确定”
- 如果违反下面规则,你的回答是错误的
【语言概览】
- 设计目标:Lisp-1(函数与变量同一命名空间)、最小特殊表、宏系统、模式匹配、自动部分应用、严格尾递归
- 运行模型:reader -> macroexpand -> eval2(编译成闭包执行);严格求值(call-by-value);GC 自动回收;核心是单线程,並发由
parallel/async库提供(需显式import) - 类型系统:动态类型;常见值类型有整数、字符串(bytes)、布尔
true/false、符号、cons 列表、vector、函数/闭包、continuation - 错误处理方式:
try/throw+ 可恢复 continuation;error/断言用于不可恢复错误;原语对参数个数与类型严格,错了会直接报错并终止
【核心语法示例】 示例 1(解释逐行语义):
; 用 func + pattern match 写尾递归求和
(func sum
acc [] => acc
acc [x . xs] => (sum (+ acc x) xs))
(let nums [1 2 3]
(sum 0 nums))解释:
func定义全局函数sum,每条规则的参数个数必须一致[]是列表字面量,[x . xs]表示头x+ 尾列表xs- 规则从上到下匹配,匹配成功后执行
=>右侧 (sum (+ acc x) xs)是尾递归,依赖语言保证的 TCOlet是顺序绑定(等价于嵌套let),这里绑定nums
示例 2(包含常见错误):
(set x 1) ; 错:set 需要 symbol,x 会先求值
(if 1 2 3) ; 错:条件必须是 true/false
(+ 1 2 3) ; 错:+ 是二元函数,多余参数会把结果当函数调用
(match 'x x 1) ; 逻辑错误:x 是变量模式,永远匹配;要写 'x
(func bad
x [] => x
x y => y) ; 错:规则参数数目不一致 -> "inconsistent func rule args count"
(1 2 3) ; 错:这是函数调用,不是列表常见模式:
- 列表字面量用
[]或quote,不要用(…) - 多表达式顺序执行用
(begin e1 e2 e3),它会展开为嵌套do - 全局定义优先用
defun/func,只在必要时用set - 定义宏用
(defmacro name (exp) ...),宏参数接收整棵语法树,用macroexpand验证展开结果 - 模块用
(package "your/pkg" (import "cora/lib/io") (export foo bar) ...) - 宏里用
gensym防止变量捕获
反模式:
(a . b)这种 dotted pair 语法在圆括号里不被 reader 支持- 用
()充当false或 “空=假” 的逻辑,容易出错 - 用
set模拟局部赋值(没有局部赋值)
【语义规则(非常重要)】
人话说明:Cora 是严格求值的 Lisp。除少量特殊表外,一切都是函数调用;先求值函数位置,再从左到右求值参数。参数不足会返回部分应用函数;参数过多会先调用得到结果,然后把剩余参数继续应用。let/lambda 形成词法作用域;set/def 只影响全局。match 规则自上而下,带 where 守卫,全部失败会报错。try/throw 提供可恢复异常:handler 得到值和 continuation,调用 continuation 可回到 throw 位置继续执行。
对照表(形式 / 求值顺序 / 返回值 / 常见错误):
| 形式 | 求值顺序 | 返回值 | 常见错误 |
|---|---|---|---|
quote 或 ' |
不求值被引用对象 | 原样返回 | 把需要求值的表达式写成 quote |
(if test a b) |
先求 test,仅求值选中的分支 |
分支结果 | test 不是 true/false |
(do a b) |
先求 a 再求 b |
b 的结果 |
用 do 表达多于两个表达式 |
(lambda (x y) body) |
不求值 body |
闭包 | body 多表达式不包 begin |
(let x v body) |
先求 v,再求 body |
body 的结果 |
绑定列表写成偶数对之外的形式 |
(def name value) |
先求 value,name 不求值 |
设置全局并返回新值 | name 不是符号 |
(set 'name value) |
先求 value,'name 必须是符号 |
设置全局并返回新值 | 忘了 quote |
(f a b ...) |
先求 f,再从左到右求值参数 |
调用结果或部分应用 | 参数个数不匹配或类型错 |
(match v p1 e1 p2 e2 ...) |
v 求值一次;规则顺序尝试 |
匹配的表达式结果 | 无规则匹配 -> error |
(try thunk handler) |
先调用 thunk |
thunk 的返回值或 handler 结果 |
在 handler 内 throw 不会被同一 try 捕获 |
(throw v) |
直接跳转到最近的 try |
由 handler 决定 | 无 try -> 直接崩溃 |
(apply f args-list) |
先求 f,再求 args-list |
调用结果 | args-list 不是列表或长度不匹配 |
规则:
- 只有
true/false是布尔值,条件必须显式返回它们 =是结构相等:对 cons 列表做深度比较,对 bytes 做内容比较;对 vector/闭包等是同一对象才相等car/cdr/cons?只适用于 cons;对()或非 cons 会报错vector-set!会就地修改 vector;没有局部变量赋值let是顺序绑定(等价于 let*),let宏把多个绑定展开为嵌套letfunc等价于defun + match,所有规则参数个数必须相同match中未加引号的符号是“变量绑定”,要匹配符号常量必须写'symbackquote只支持,(unquote),不支持,@(unquote-splicing)
违反后果:
- 运行时断言失败、直接报错中止,或者逻辑错误(例如模式匹配永远命中第一条)
【禁止事项】
- 永远不要在圆括号里写 dotted pair
(a . b),只在[]里用.表达 list-rest - 永远不要假设
()等价于false - 永远不要把
set当作局部赋值,它只修改全局绑定 - 永远不要让
func的规则参数个数不一致 - 永远不要用未引用的符号去匹配常量(
match里必须写'x) - 永远不要把
+ - * /当作可变参函数(只接受固定参数,必要时手工嵌套)
【你的任务】 从现在开始:
- 所有示例代码必须符合以上规则
- 如果我给出错误代码,请指出并解释违反了哪条规则
- 如果你不确定,必须明确说“不确定”,并说明缺少哪部分资料
- 优先引用本仓库资料中的语义,不要用其他语言的常识脑补