diff options
author | Nadrieril | 2019-04-19 22:29:41 +0200 |
---|---|---|
committer | Nadrieril | 2019-04-19 22:30:43 +0200 |
commit | deeec70f6bd602f0f178766b8e89ec2585c279c0 (patch) | |
tree | 44a7201968a94eb165e12dd5a957873e3c46728b /dhall | |
parent | 976647b1a887ee0753bf39dacd9528f62d9e086a (diff) |
Embrace WHNF
Diffstat (limited to 'dhall')
-rw-r--r-- | dhall/src/normalize.rs | 156 |
1 files changed, 77 insertions, 79 deletions
diff --git a/dhall/src/normalize.rs b/dhall/src/normalize.rs index bd479d0..64c17c3 100644 --- a/dhall/src/normalize.rs +++ b/dhall/src/normalize.rs @@ -157,7 +157,7 @@ where ), // // fold/build fusion // (ListFold, [_, App(box Builtin(ListBuild), [_, x, rest..]), rest..]) => { - // normalize_value(&App(bx(x.clone()), rest.to_vec())) + // normalize_whnf(&App(bx(x.clone()), rest.to_vec())) // } (OptionalFold, [_, NEOptionalLit(x), _, just, _, rest..]) => { let x = x.clone(); @@ -169,7 +169,7 @@ where } // // fold/build fusion // (OptionalFold, [_, App(box Builtin(OptionalBuild), [_, x, rest..]), rest..]) => { - // normalize_value(&App(bx(x.clone()), rest.to_vec())) + // normalize_whnf(&App(bx(x.clone()), rest.to_vec())) // } (NaturalBuild, [g, rest..]) => { 'ret: { @@ -226,71 +226,71 @@ where // expr: SubExpr<X, Normalized<'static>>, // } // impl ContextualizedSubExpr { -// fn normalize_value(self) -> Value { -// normalize_value(&self.ctx, self.expr) +// fn normalize_whnf(self) -> WHNF { +// normalize_whnf(&self.ctx, self.expr) // } // fn normalize_expr(self) -> OutputSubExpr { -// normalize_value(&self.ctx, self.expr).into_expr(&self.ctx) +// normalize_whnf(&self.ctx, self.expr).into_expr(&self.ctx) // } // } /// A semantic value. This is partially redundant with `dhall_core::Expr`, on purpose. `Expr` should /// be limited to syntactic expressions: either written by the user or meant to be printed. /// The rule is the following: we must _not_ construct values of type `Expr` while normalizing or -/// typechecking, but only construct `Value`s. +/// typechecking, but only construct `WHNF`s. /// -/// Values usually store subexpressions unnormalized, to enable lazy normalization. They -/// approximate what's called Weak Head Normal-Form (WHNF). This means that the expression is -/// normalized as little as possible, but just enough to know the first constructor of the normal -/// form. This is identical to full normalization for simple types like integers, but for example -/// for a record literal this means knowing just the field names. +/// WHNFs usually store subexpressions unnormalized, to enable lazy normalization. They approximate +/// what's called Weak Head Normal-Form (WHNF). This means that the expression is normalized as +/// little as possible, but just enough to know the first constructor of the normal form. This is +/// identical to full normalization for simple types like integers, but for e.g. a record literal +/// this means knowing just the field names. #[derive(Debug, Clone)] -enum Value { +enum WHNF { Closure(Closure), RecordLit(NormalizationContext, BTreeMap<Label, InputSubExpr>), UnionType(NormalizationContext, BTreeMap<Label, Option<InputSubExpr>>), Expr(OutputSubExpr), } -impl Value { +impl WHNF { /// Convert the value back to a (normalized) syntactic expression fn normalize_to_expr(self) -> OutputSubExpr { match self { - Value::Closure(c) => c.normalize_to_expr(), - Value::RecordLit(ctx, kvs) => rc(ExprF::RecordLit( + WHNF::Closure(c) => c.normalize_to_expr(), + WHNF::RecordLit(ctx, kvs) => rc(ExprF::RecordLit( kvs.into_iter() .map(|(k, v)| { - (k, normalize_value(&ctx, v).normalize_to_expr()) + (k, normalize_whnf(&ctx, v).normalize_to_expr()) }) .collect(), )), - Value::UnionType(ctx, kvs) => rc(ExprF::UnionType( + WHNF::UnionType(ctx, kvs) => rc(ExprF::UnionType( kvs.into_iter() .map(|(k, v)| { ( k, v.map(|v| { - normalize_value(&ctx, v).normalize_to_expr() + normalize_whnf(&ctx, v).normalize_to_expr() }), ) }) .collect(), )), - Value::Expr(e) => e, + WHNF::Expr(e) => e, } } fn shift0(self, delta: isize, label: &Label) -> Self { match self { - Value::Closure(c) => Value::Closure(c.shift0(delta, label)), - Value::Expr(e) => Value::Expr(shift0(delta, label, &e)), - Value::RecordLit(ctx, kvs) => Value::RecordLit( + WHNF::Closure(c) => WHNF::Closure(c.shift0(delta, label)), + WHNF::Expr(e) => WHNF::Expr(shift0(delta, label, &e)), + WHNF::RecordLit(ctx, kvs) => WHNF::RecordLit( ctx, kvs.into_iter() .map(|(k, v)| (k, shift0(delta, label, &v))) .collect(), ), - Value::UnionType(ctx, kvs) => Value::UnionType( + WHNF::UnionType(ctx, kvs) => WHNF::UnionType( ctx, kvs.into_iter() .map(|(k, v)| (k, v.map(|v| shift0(delta, label, &v)))) @@ -303,7 +303,7 @@ impl Value { #[derive(Debug, Clone)] enum Closure { Lam(NormalizationContext, Label, InputSubExpr, InputSubExpr), - AppliedBuiltin(NormalizationContext, Builtin, Vec<Value>), + AppliedBuiltin(NormalizationContext, Builtin, Vec<WHNF>), UnionConstructor( NormalizationContext, Label, @@ -313,11 +313,11 @@ enum Closure { impl Closure { /// Apply the closure to a value - fn app(self, val: Value) -> Value { + fn app(self, val: WHNF) -> WHNF { match self { Closure::Lam(ctx, x, _, e) => { let ctx2 = ctx.insert(&x, val); - normalize_value(&ctx2, e) + normalize_whnf(&ctx2, e) } Closure::AppliedBuiltin(ctx, b, mut args) => { args.push(val); @@ -328,7 +328,7 @@ impl Closure { match apply_builtin(b, &args_unrolled).into_value(&ctx) { Some(v) => v, None => { - Value::Closure(Closure::AppliedBuiltin(ctx, b, args)) + WHNF::Closure(Closure::AppliedBuiltin(ctx, b, args)) } } } @@ -339,16 +339,12 @@ impl Closure { ( k, v.map(|v| { - normalize_value(&ctx, v).normalize_to_expr() + normalize_whnf(&ctx, v).normalize_to_expr() }), ) }) .collect(); - Value::Expr(rc(ExprF::UnionLit( - l, - val.normalize_to_expr(), - kts, - ))) + WHNF::Expr(rc(ExprF::UnionLit(l, val.normalize_to_expr(), kts))) } } } @@ -360,8 +356,8 @@ impl Closure { let ctx2 = ctx.skip(&x); rc(ExprF::Lam( x.clone(), - normalize_value(&ctx, t).normalize_to_expr(), - normalize_value(&ctx2, e).normalize_to_expr(), + normalize_whnf(&ctx, t).normalize_to_expr(), + normalize_whnf(&ctx2, e).normalize_to_expr(), )) } Closure::AppliedBuiltin(_ctx, b, args) => { @@ -382,7 +378,7 @@ impl Closure { ( k, v.map(|v| { - normalize_value(&ctx, v).normalize_to_expr() + normalize_whnf(&ctx, v).normalize_to_expr() }), ) }) @@ -418,7 +414,7 @@ impl Closure { #[derive(Debug, Clone)] enum EnvItem { - Expr(Value), + Expr(WHNF), Skip(usize), } @@ -429,7 +425,7 @@ impl NormalizationContext { fn new() -> NormalizationContext { NormalizationContext(Context::new()) } - fn insert(&self, x: &Label, e: Value) -> NormalizationContext { + fn insert(&self, x: &Label, e: WHNF) -> NormalizationContext { NormalizationContext(self.0.insert(x.clone(), EnvItem::Expr(e))) } fn skip(&self, x: &Label) -> NormalizationContext { @@ -446,14 +442,14 @@ impl NormalizationContext { .insert(x.clone(), EnvItem::Skip(0)), ) } - fn lookup(&self, var: &V<Label>) -> Value { + fn lookup(&self, var: &V<Label>) -> WHNF { let V(x, n) = var; match self.0.lookup(x, *n) { Some(EnvItem::Expr(e)) => e.clone(), Some(EnvItem::Skip(m)) => { - Value::Expr(rc(ExprF::Var(V(x.clone(), *m)))) + WHNF::Expr(rc(ExprF::Var(V(x.clone(), *m)))) } - None => Value::Expr(rc(ExprF::Var(V(x.clone(), *n)))), + None => WHNF::Expr(rc(ExprF::Var(V(x.clone(), *n)))), } } } @@ -471,10 +467,10 @@ enum WhatNext<'a, N, E> { } impl<'a> WhatNext<'a, X, X> { - fn into_value(self, ctx: &NormalizationContext) -> Option<Value> { + fn into_value(self, ctx: &NormalizationContext) -> Option<WHNF> { use WhatNext::*; match self { - Continue(e) => Some(normalize_value(ctx, e.embed_absurd().roll())), + Continue(e) => Some(normalize_whnf(ctx, e.embed_absurd().roll())), Done(e) => Some(reval(ctx, e.roll())), DoneRef(e) => Some(reval(ctx, e.roll())), DoneRefSub(e) => Some(reval(ctx, e.clone())), @@ -483,28 +479,29 @@ impl<'a> WhatNext<'a, X, X> { } } -fn reval(ctx: &NormalizationContext, expr: OutputSubExpr) -> Value { +fn reval(ctx: &NormalizationContext, expr: OutputSubExpr) -> WHNF { match expr.as_ref().embed_absurd() { ExprF::Lam(x, t, e) => { - Value::Closure(Closure::Lam(ctx.clone(), x, t, e)) + WHNF::Closure(Closure::Lam(ctx.clone(), x, t, e)) } ExprF::Builtin(b) => { - Value::Closure(Closure::AppliedBuiltin(ctx.clone(), b, vec![])) + WHNF::Closure(Closure::AppliedBuiltin(ctx.clone(), b, vec![])) } - ExprF::RecordLit(kvs) => Value::RecordLit(ctx.clone(), kvs), - ExprF::UnionType(kvs) => Value::UnionType(ctx.clone(), kvs), - _ => Value::Expr(expr), + ExprF::RecordLit(kvs) => WHNF::RecordLit(ctx.clone(), kvs), + ExprF::UnionType(kvs) => WHNF::UnionType(ctx.clone(), kvs), + _ => WHNF::Expr(expr), } } -fn normalize_value(ctx: &NormalizationContext, expr: InputSubExpr) -> Value { +/// Reduces the imput expression to WHNF. See doc on `WHNF` for details. +fn normalize_whnf(ctx: &NormalizationContext, expr: InputSubExpr) -> WHNF { let e = match expr.as_ref() { ExprF::Let(x, _, r, b) => { - let r = normalize_value(ctx, r.clone()); - return normalize_value(&ctx.insert(x, r), b.clone()); + let r = normalize_whnf(ctx, r.clone()); + return normalize_whnf(&ctx.insert(x, r), b.clone()); } ExprF::Lam(x, t, e) => { - return Value::Closure(Closure::Lam( + return WHNF::Closure(Closure::Lam( ctx.clone(), x.clone(), t.clone(), @@ -512,26 +509,26 @@ fn normalize_value(ctx: &NormalizationContext, expr: InputSubExpr) -> Value { )) } ExprF::Builtin(b) => { - return Value::Closure(Closure::AppliedBuiltin( + return WHNF::Closure(Closure::AppliedBuiltin( ctx.clone(), *b, vec![], )) } ExprF::RecordLit(kvs) => { - return Value::RecordLit(ctx.clone(), kvs.clone()) + return WHNF::RecordLit(ctx.clone(), kvs.clone()) } ExprF::UnionType(kvs) => { - return Value::UnionType(ctx.clone(), kvs.clone()) + return WHNF::UnionType(ctx.clone(), kvs.clone()) } e => e, }; // Recursively normalize all subexpressions - let expr: ExprF<Value, Label, X, Normalized<'static>> = e + let expr: ExprF<WHNF, Label, X, Normalized<'static>> = e .map_ref_with_special_handling_of_binders( - |e| normalize_value(ctx, e.clone()), - |x, e| normalize_value(&ctx.skip(x), e.clone()), + |e| normalize_whnf(ctx, e.clone()), + |x, e| normalize_whnf(&ctx.skip(x), e.clone()), X::clone, Normalized::clone, Label::clone, @@ -543,8 +540,8 @@ fn normalize_value(ctx: &NormalizationContext, expr: InputSubExpr) -> Value { /// When all sub-expressions have been (partially) normalized, eval the remaining toplevel layer. fn normalize_last_layer( ctx: &NormalizationContext, - expr: ExprF<Value, Label, X, Normalized<'static>>, -) -> Value { + expr: ExprF<WHNF, Label, X, Normalized<'static>>, +) -> WHNF { use dhall_core::BinOp::*; use dhall_core::ExprF::*; use WhatNext::*; @@ -553,25 +550,26 @@ fn normalize_last_layer( Var(v) => return ctx.lookup(&v), Annot(x, _) => return x, Note(_, e) => return e, - App(Value::Closure(c), a) => return c.app(a), - Field(Value::UnionType(ctx, kts), l) => { - return Value::Closure(Closure::UnionConstructor(ctx, l, kts)) + App(WHNF::Closure(c), a) => return c.app(a), + Field(WHNF::UnionType(ctx, kts), l) => { + return WHNF::Closure(Closure::UnionConstructor(ctx, l, kts)) + } + Field(WHNF::RecordLit(record_ctx, mut kvs), l) => { + match kvs.remove(&l) { + Some(r) => return normalize_whnf(&record_ctx, r), + // Return ownership + None => Field(WHNF::RecordLit(record_ctx, kvs), l), + } } - Field(Value::RecordLit(record_ctx, mut kvs), l) => match kvs.remove(&l) - { - Some(r) => return normalize_value(&record_ctx, r), - // Return ownership - None => Field(Value::RecordLit(record_ctx, kvs), l), - }, // Always simplify `x.{}` to `{}` Projection(_, ls) if ls.is_empty() => { - return Value::RecordLit( + return WHNF::RecordLit( ctx.clone(), std::collections::BTreeMap::new(), ) } - Projection(Value::RecordLit(record_ctx, mut kvs), ls) => { - return Value::RecordLit( + Projection(WHNF::RecordLit(record_ctx, mut kvs), ls) => { + return WHNF::RecordLit( record_ctx, ls.into_iter() .filter_map(|l| kvs.remove(&l).map(|x| (l, x))) @@ -579,15 +577,15 @@ fn normalize_last_layer( ) } Merge( - Value::RecordLit(record_ctx, mut handlers), - Value::Closure(Closure::UnionConstructor(ctor_ctx, l, kts)), + WHNF::RecordLit(record_ctx, mut handlers), + WHNF::Closure(Closure::UnionConstructor(ctor_ctx, l, kts)), t, ) => match handlers.remove(&l) { - Some(h) => return normalize_value(ctx, h), + Some(h) => return normalize_whnf(ctx, h), // Return ownership None => Merge( - Value::RecordLit(record_ctx, handlers), - Value::Closure(Closure::UnionConstructor(ctor_ctx, l, kts)), + WHNF::RecordLit(record_ctx, handlers), + WHNF::Closure(Closure::UnionConstructor(ctor_ctx, l, kts)), t, ), }, @@ -667,7 +665,7 @@ fn normalize_last_layer( /// leave ill-typed sub-expressions unevaluated. /// fn normalize(e: InputSubExpr) -> OutputSubExpr { - normalize_value(&NormalizationContext::new(), e).normalize_to_expr() + normalize_whnf(&NormalizationContext::new(), e).normalize_to_expr() } #[cfg(test)] |