stlc API
stlc 包是基于共享命名语法的简单类型 lambda 演算:类型、带类型常量的签名、类型上下文、双向类型推断与检查、有步数上限的操作式范式化,以及到 β-范式、η-长形式的带类型求值范式化(NbE)。
import {
"Luna-Flow/type_theory/core",
"Luna-Flow/type_theory/syntax",
"Luna-Flow/type_theory/rewrite",
"Luna-Flow/type_theory/stlc",
}
项是 @syntax.Term[Atom]:Bind(x, b) 是 (不带类型标注),Apply 是应用,Variable 是变量,Value 持有一个 Atom。类型规则和 NbE 算法见 stlc 设计。
语法
Atom
Atom 是该演算中的常量。
pub(all) enum Atom {
UnitLit
Const(@core.Name)
} derive(Eq, @debug.Debug)
pub fn Atom::equal(Self, Self) -> Bool
UnitLit 是单位值 ;Const(c) 是一个类型在 Signature 中声明的常量。
Term
Term 是 STLC 项的类型。
pub type Term = @syntax.Term[Atom]
它是一个别名,因此 syntax、substitution 和 rewrite 的全部内容都适用于 STLC 项。
Ty
Ty 是简单类型。
pub(all) enum Ty {
Base(@core.Name)
Unit
Arrow(Ty, Ty)
} derive(Eq, @debug.Debug)
pub fn Ty::equal(Self, Self) -> Bool
Base(b) 是未解释的基本类型,Unit 是单位类型,Arrow(a, b) 是函数类型 。类型按结构进行比较。
签名与上下文
Signature
Signature 为常量指定类型。
pub struct Signature {
entries : Array[(@core.Name, Ty)]
} derive(Eq, @debug.Debug)
pub fn Signature::empty() -> Self
#alias(extend, deprecated)
pub fn Signature::extend_with(Self, @core.Name, Ty) -> Self
pub fn Signature::lookup(Self, @core.Name) -> Ty?
pub fn Signature::to_array(Self) -> Array[(@core.Name, Ty)]
pub fn Signature::equal(Self, Self) -> Bool
extend_with(c, ty) 返回添加了 c : ty 的新签名;同名的较晚条目会遮蔽较早的条目,lookup 返回最新的那个。to_array 按插入顺序返回条目的副本。
TypeContext
TypeContext 为自由变量指定类型。
pub struct TypeContext {
entries : Array[(@core.Name, Ty)]
} derive(Eq, @debug.Debug)
pub fn TypeContext::empty() -> Self
#alias(extend, deprecated)
pub fn TypeContext::extend_with(Self, @core.Name, Ty) -> Self
pub fn TypeContext::lookup(Self, @core.Name) -> Ty?
pub fn TypeContext::to_array(Self) -> Array[(@core.Name, Ty)]
pub fn TypeContext::equal(Self, Self) -> Bool
同样的遮蔽规则也适用:类型检查器在进入 lambda 时扩展上下文,因此一个名字由最近的绑定子决定。
test "signatures and contexts" {
let c = @core.Name::new("c")
let x = @core.Name::new("x")
let a = @stlc.Ty::Base(@core.Name::new("A"))
let sig = @stlc.Signature::empty().extend_with(c, @stlc.Ty::Arrow(a, a))
let ctx = @stlc.TypeContext::empty().extend_with(x, a).extend_with(x, @stlc.Ty::Unit)
assert_eq(sig.lookup(c), Some(@stlc.Ty::Arrow(a, a)))
assert_eq(ctx.lookup(x), Some(@stlc.Ty::Unit))
assert_eq(ctx.to_array().length(), 2)
}
错误
TypeError
TypeError 报告项被拒绝的原因。
pub(all) enum TypeError {
UnboundVariable(@core.Name)
UnknownConstant(@core.Name)
CannotInferLambda
ExpectedFunction(Ty)
TypeMismatch(expected~ : Ty, actual~ : Ty)
EmptyApplication
ScopeError(@debruijn.ScopeError)
NormalizationError(message~ : String)
} derive(Eq, @debug.Debug)
pub fn TypeError::equal(Self, Self) -> Bool
| 情形 | 含义 |
|---|---|
UnboundVariable(x) | x 不在上下文中。 |
UnknownConstant(c) | c 不在签名中。 |
CannotInferLambda | lambda 出现在必须推断其类型的位置。 |
ExpectedFunction(ty) | 对非函数类型 ty 的项进行了应用。 |
TypeMismatch(expected, actual) | 推断出的类型与期望的类型不同。 |
EmptyApplication | Apply(head, []) 没有参数。 |
ScopeError(e) | 为 De Bruijn 作用域错误保留;当前的函数不会产生。 |
NormalizationError(message) | 带类型 NbE 的内部不变式失败;对已检查的输入不应出现。 |
类型检查
infer
infer 合成项的类型。
pub fn infer(Signature, TypeContext, @syntax.Term[Atom]) -> Result[Ty, TypeError]
单位字面量的类型是 Unit,常量和变量具有其声明的类型;对于应用 f a_1 … a_n,在将每个参数对照相应的定义域检查之后,其类型为 f 的结果类型。单独的 lambda 无法推断(CannotInferLambda)。直接应用于参数的 lambda 的推断方式是:先推断第一个参数的类型,再在该假设下推断体的类型。
check
check 将项对照类型进行检验。
pub fn check(Signature, TypeContext, @syntax.Term[Atom], Ty) -> Result[Unit, TypeError]
lambda 对照箭头类型进行检查时,会以定义域作为参数的类型,将体对照陪域进行检查。其他所有项都先推断类型,再用 == 比较;若不同则给出 TypeMismatch。
test "infer and check" {
let x = @core.Name::new("x")
let f = @core.Name::new("f")
let a = @stlc.Ty::Base(@core.Name::new("A"))
let id : @stlc.Term = Bind(x, Variable(x))
let sig = @stlc.Signature::empty()
let ctx = @stlc.TypeContext::empty()
assert_eq(@stlc.check(sig, ctx, id, @stlc.Ty::Arrow(a, a)), Ok(()))
assert_eq(@stlc.infer(sig, ctx, id), Err(@stlc.TypeError::CannotInferLambda))
let ctx_f = ctx.extend_with(f, @stlc.Ty::Arrow(a, a))
let bad : @stlc.Term = Apply(Variable(f), [Value(@stlc.Atom::UnitLit)])
assert_eq(
@stlc.infer(sig, ctx_f, bad),
Err(@stlc.TypeError::TypeMismatch(expected=a, actual=@stlc.Ty::Unit)),
)
}
范式化
normalize_checked
normalize_checked 将项对照类型进行检查,然后用无类型的 β-η 归约器对其范式化。
pub fn normalize_checked(Signature, TypeContext, @syntax.Term[Atom], Ty, Int) -> Result[@rewrite.NormalizationResult[Atom], TypeError]
遇到类型错误时,它返回 Err 而不进行归约。否则返回 Ok(@lambda.normalize(term, max_steps)):带步数上限的正规序 β-η 归约。其范式是 β-范式且 η-短的。
normalize_eta_long
normalize_eta_long 将项对照类型进行检查,并返回其 β-范式、η-长形式,该形式通过带类型的求值范式化计算得到。
pub fn normalize_eta_long(Signature, TypeContext, @syntax.Term[Atom], Ty) -> Result[@syntax.Term[Atom], TypeError]
在结果中,每个箭头类型的子项都是 lambda,每个应用的头部都是变量或常量。上下文中的变量和签名中的常量保持原样,并按其类型进行 η-展开。不需要步数上限:良类型的项总能范式化。新的绑定子名称为 x、x_1、…,其选取会避开项和上下文中的所有名字。
test "beta-normal eta-long form" {
let f = @core.Name::new("f")
let x = @core.Name::new("x")
let a = @stlc.Ty::Base(@core.Name::new("A"))
let ctx = @stlc.TypeContext::empty().extend_with(f, @stlc.Ty::Arrow(a, a))
let sig = @stlc.Signature::empty()
// f is eta-expanded to λx. f x
let expected : @stlc.Term = Bind(x, Apply(Variable(f), [Variable(x)]))
match @stlc.normalize_eta_long(sig, ctx, Variable(f), @stlc.Ty::Arrow(a, a)) {
Ok(normal) => assert_true(@syntax.alpha_equal(normal, expected))
Err(_) => fail("well typed")
}
// the operational normalizer contracts the redex but does not expand
let redex : @stlc.Term = Apply(Bind(x, Variable(x)), [Variable(f)])
assert_eq(
@stlc.normalize_checked(sig, ctx, redex, @stlc.Ty::Arrow(a, a), 10),
Ok(NormalForm(term=Variable(f), steps=1)),
)
}
已弃用
| 已弃用 | 替代项 |
|---|---|
Signature::extend | Signature::extend_with |
TypeContext::extend | TypeContext::extend_with |
本包中各类型上隐藏的方法形式 not_equal 和 to_repr 已弃用;请使用 != 和 Repr(x)。