; Substitution in lambda-calculus via S/K/I combinators. Extremely slow, as
; abstraction elimination does not pay attention to whether variables are free
; in an expression before introducing 'S'.
;
; Provides an example of how to implement substitution by embedding in a
; 'richer' data-type and then mapping back to syntax.
(datatype Expr
(Var String :cost 100)
(Abs String Expr)
(If Expr Expr Expr)
(N i64)
(Add Expr Expr)
(App Expr Expr))
(constructor TConst () Expr)
(let $T (TConst))
(constructor FConst () Expr)
(let $F (FConst))
; (\x. (if x then 0 else 1) + 2) false
(let $test
(App
(Abs "x" (Add (If (Var "x") (N 0) (N 1)) (N 2))) $F))
(datatype CExpr
(CVar String :cost 10000) ; (variables that haven't been eliminated yet)
(CAbs String CExpr :cost 10000) ; (abstractions that haven't been eliminated yet)
(CN i64)
(CApp CExpr CExpr))
(constructor CTConst () CExpr)
(let $CT (CTConst))
(constructor CFConst () CExpr)
(let $CF (CFConst))
(constructor CIfConst () CExpr)
(let $CIf (CIfConst))
(constructor CAddConst () CExpr)
(let $CAdd (CAddConst))
(constructor SConst () CExpr)
(let $S (SConst))
(constructor KConst () CExpr)
(let $K (KConst))
(constructor IConst () CExpr)
(let $I (IConst))
;;;; Conversion functions
(constructor Comb (Expr) CExpr :cost 1000000)
(constructor Uncomb (CExpr) Expr)
(rewrite (Comb (Uncomb cx)) cx)
(rewrite (Uncomb (Comb x)) x)
; Mechanical mappings back and forth.
; Note: we avoid resugaring S/K/I
(rule ((= x (N n))) ((union (Comb x) (CN n))))
(rule ((= cx (CN n))) ((union (Uncomb cx) (N n))))
(rule ((= x $T)) ((union (Comb x) $CT)))
(rule ((= cx $CT)) ((union (Uncomb cx) $T)))
(rule ((= x $F)) ((union (Comb x) $CF)))
(rule ((= cx $CF)) ((union (Uncomb cx) $F)))
(rule ((= x (If c t f)))
((union (Comb x) (CApp (CApp (CApp $CIf (Comb c)) (Comb t)) (Comb f)))))
(rule ((= cx (CApp (CApp (CApp $CIf cc) ct) cf)))
((union (Uncomb cx) (If (Uncomb cc) (Uncomb ct) (Uncomb cf)))))
(rule ((= x (Add l r)))
((union (Comb x) (CApp (CApp $CAdd (Comb l)) (Comb r)))))
(rule ((= cx (CApp (CApp $CAdd cl) cr)))
((union (Uncomb cx) (Add (Uncomb cl) (Uncomb cr)))))
(rule ((= x (App f a))) ((union (Comb x) (CApp (Comb f) (Comb a)))))
(rule ((= x (Var v))) ((union (Comb x) (CVar v))))
(rule ((= x (Abs v body))) ((union (Comb x) (CAbs v (Comb body)))))
;;;; Abstraction Elimination
(rewrite (CAbs v (CVar v)) $I)
; Hacks, could be replaced by !free computation.
(rewrite (CAbs v1 (CVar v2)) (CApp $K (CVar v2))
:when ((!= v1 v2)))
(rewrite (CAbs v (CN n)) (CApp $K (CN n)))
(rewrite (CAbs v $CT) (CApp $K $CT))
(rewrite (CAbs v $CF) (CApp $K $CF))
(rewrite (CAbs v $CIf) (CApp $K $CIf))
(rewrite (CAbs v $CAdd) (CApp $K $CAdd))
(rewrite (CAbs v (CApp x y)) (CApp (CApp $S (CAbs v x)) (CAbs v y)))
; May be needed for multiple nested variables
(rewrite (CAbs v (CApp $K (CVar v))) $K)
;;;; Primitive Evaluation rules (letd on "surface syntax")
(rewrite (If $T t f) t)
(rewrite (If $F t f) f)
(rewrite (Add (N n) (N m)) (N (+ n m)))
;;;; Substitution Rules (letd on the combinator representation)
(rewrite (CApp $I cx) cx)
(rewrite (CApp (CApp $K cx) cy) cx)
; Without demand, this can cause an explosion in DB size.
(rewrite (CApp (CApp (CApp $S cx) cy) cz) (CApp (CApp cx cz) (CApp cy cz)))
(run 11)
(extract (Comb $test))
(check (= $test (N 3)))