From 7e2cb02cb9e846b9502de7f677e69ebcc710cdce Mon Sep 17 00:00:00 2001 From: JJ Date: Wed, 23 Oct 2024 17:26:11 -0700 Subject: refactor all implementations to use contracts --- stlc-ext.rkt | 211 ++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 138 insertions(+), 73 deletions(-) (limited to 'stlc-ext.rkt') diff --git a/stlc-ext.rkt b/stlc-ext.rkt index 2662135..c76db93 100644 --- a/stlc-ext.rkt +++ b/stlc-ext.rkt @@ -1,107 +1,172 @@ #lang racket (require "lib.rkt") (require "base.rkt") -(provide interpret check infer expand equiv-type equiv-term) +(provide (all-defined-out)) ;; The Simply-Typed Lambda Calculus, with simple extensions ;; Unit/String/Natural/Boolean, pairs, sums, lists, ascryption -;; (interpret Expr Table[Sym, Expr]): Value +;; Checks an expression for syntactic well-formedness. +(define (stlc-ext/expr? expr) + (match expr + [(or 'sole 'nil) #t] + [s #:when (string? s) #t] + [n #:when (natural? n) #t] + [b #:when (boolean? b) #t] + [x #:when (symbol? x) #t] + + [(or + `(inc ,e) + `(car ,e) `(cdr ,e) + `(inl ,e) `(inr ,e) + `(head ,e) `(tail ,e) `(nil? ,e)) + (stlc-ext/expr? e)] + [(or + `(pair ,e1 ,e2) + `(cons ,e1 ,e2) + `(,e1 ,e2)) + (and (stlc-ext/expr? e1) (stlc-ext/expr? e2))] + + [`(if ,c ,e1 ,e2) + (and (stlc-ext/expr? c) (stlc-ext/expr? e1) (stlc-ext/expr? e2))] + [`(case ,e (,x1 ⇒ ,e1) (,x2 ⇒ ,e2)) + (and (stlc-ext/expr? e) (stlc-ext/expr? e1) (stlc-ext/expr? e2) + (symbol? x1) (symbol? x2))] + [`(type ,t1 ,t2 ,e) + (and (stlc-ext/type? t1) (stlc-ext/type? t2) (stlc-ext/expr? e))] + [`(λ (,x : ,t) ,e) + (and (symbol? x) (stlc-ext/type? t) (stlc-ext/expr? e))] + [_ #f])) + +;; Checks a type for syntactic well-formedness. +(define (stlc-ext/type? type) + (match type + [t #:when (symbol? t) #t] + [`(List ,t) (stlc-ext/type? t)] + [(or + `(,t1 → ,t2) + `(,t1 × ,t2) + `(,t1 ⊕ ,t2)) + (and (stlc-ext/type? t1) (stlc-ext/type? t2))] + [_ #f])) + +;; Checks a value for syntactic well-formedness. +(define (stlc-ext/value? value) + (match value + [(or 'sole 'nil) #t] + [s #:when (string? s) #t] + [n #:when (natural? n) #t] + [b #:when (boolean? b) #t] + [x #:when (symbol? x) #t] + [(or + `(pair ,v1 ,v2) + `(cons ,v1 ,v2) + `(,v1 ,v2)) + (and (stlc-ext/value? v1) (stlc-ext/value? v2))] + [`(λ ,x ,e ,env) + (and (symbol? x) (stlc-ext/expr? e) (dict? env))] + [_ #f])) + +;; Interprets an expression down to a value, in a given context. (define (interpret expr) - (interpret-core (strip (desugar expr)) #hash())) -(define (interpret-core expr Γ) + (interpret/core (desugar expr) #hash())) +(define/contract (interpret/core expr Γ) + (-> stlc-ext/expr? dict? stlc-ext/value?) (match expr ['sole 'sole] [s #:when (string? s) s] [n #:when (natural? n) n] [b #:when (boolean? b) b] [x #:when (dict-has-key? Γ x) (dict-ref Γ x)] + [f #:when (symbol? f) f] + + [`(,e : ,t) (interpret/core e Γ)] + [`(type ,t1 ,t2 ,e) (interpret/core e Γ)] [`(inc ,e) - (match (interpret-core e Γ) + (match (interpret/core e Γ) [n #:when (natural? n) (+ n 1)] [e (format "incrementing an unknown value ~a" e)])] [`(if ,c ,e1 ,e2) - (match (interpret-core c Γ) - ['#t (interpret-core e1 Γ)] - ['#f (interpret-core e2 Γ)] + (match (interpret/core c Γ) + ['#t (interpret/core e1 Γ)] + ['#f (interpret/core e2 Γ)] [e (err (format "calling if on unknown expression ~a" e))])] [`(pair ,e1 ,e2) - `(pair ,(interpret-core e1 Γ) ,(interpret-core e2 Γ))] + `(pair ,(interpret/core e1 Γ) ,(interpret/core e2 Γ))] [`(car ,e) - (match (interpret-core e Γ) + (match (interpret/core e Γ) [`(pair ,e1 ,e2) e1] [e (err (format "calling car on unknown expression ~a" e))])] [`(cdr ,e) - (match (interpret-core e Γ) + (match (interpret/core e Γ) [`(pair ,e1 ,e2) e2] [e (err (format "calling cdr on unknown expression ~a" e))])] - [`(inl ,e) `(inl ,(interpret-core e Γ))] - [`(inr ,e) `(inr ,(interpret-core e Γ))] + [`(inl ,e) `(inl ,(interpret/core e Γ))] + [`(inr ,e) `(inr ,(interpret/core e Γ))] [`(case ,e (,x1 ⇒ ,e1) (,x2 ⇒ ,e2)) - (match (interpret-core e Γ) - [`(inl ,e) (interpret-core e1 (dict-set Γ x1 e))] - [`(inr ,e) (interpret-core e2 (dict-set Γ x2 e))] + (match (interpret/core e Γ) + [`(inl ,e) (interpret/core e1 (dict-set Γ x1 e))] + [`(inr ,e) (interpret/core e2 (dict-set Γ x2 e))] [e (err (format "calling case on unknown expression ~a" e))])] ['nil 'nil] [`(nil? ,e) - (match (interpret-core e Γ) + (match (interpret/core e Γ) ['nil '#t] [`(cons ,e1 ,e2) '#f] [e (err (format "calling isnil on unknown expression ~a" e))])] [`(cons ,e1 ,e2) - `(cons ,(interpret-core e1 Γ) ,(interpret-core e2 Γ))] + `(cons ,(interpret/core e1 Γ) ,(interpret/core e2 Γ))] [`(head ,e) - (match (interpret-core e Γ) - [`(cons ,e1 ,e2) (interpret-core e1 Γ)] + (match (interpret/core e Γ) + [`(cons ,e1 ,e2) (interpret/core e1 Γ)] [e (err (format "calling head on unknown expression ~a" e))])] [`(tail ,e) - (match (interpret-core e Γ) - [`(cons ,e1 ,e2) (interpret-core e2 Γ)] + (match (interpret/core e Γ) + [`(cons ,e1 ,e2) (interpret/core e2 Γ)] [e (err (format "calling tail on unknown expression ~a" e))])] - [`(λ ,x ,e) `(λ ,x ,e ,Γ)] + [`(λ (,x : ,t) ,e) `(λ ,x ,e ,Γ)] [`(,e1 ,e2) - (match (interpret-core e1 Γ) + (match (interpret/core e1 Γ) [`(λ ,x ,e ,env) - (interpret-core e (dict-set env x (interpret-core e2 Γ)))] - [e (err (format "applying arg ~a to unknown expression ~a" e2 e))])] - - [e (err (format "interpreting an unknown expression ~a" e))])) + (interpret/core e (dict-set env x (interpret/core e2 Γ)))] + [e (err (format "applying arg ~a to unknown expression ~a" e2 e))])])) -;; (check Expr Type Table[Sym, Type]): Bool +;; Checks an expression against some type, in a given context. (define (check expr with) - (check-core (desugar expr) with #hash())) -(define (check-core expr with Γ) + (check/core (desugar expr) with #hash())) +(define/contract (check/core expr with Γ) + (-> stlc-ext/expr? stlc-ext/type? dict? boolean?) (match expr [`(type ,t1 ,t2 ,in) - (check-core in with (dict-set Γ t1 t2))] + (check/core in with (dict-set Γ t1 t2))] [`(if ,c ,e1 ,e2) - (and (check-core c 'Bool Γ) - (check-core e1 with Γ) (check-core e2 with Γ))] + (and (check/core c 'Bool Γ) + (check/core e1 with Γ) (check/core e2 with Γ))] [`(pair ,e1 ,e2) (match with - [`(,t1 × ,t2) (and (check-core e1 t1 Γ) (check-core e2 t2 Γ))] + [`(,t1 × ,t2) (and (check/core e1 t1 Γ) (check/core e2 t2 Γ))] [_ #f])] [`(inl ,e) (match with - [`(,t1 ⊕ ,t2) (check-core e t1 Γ)] + [`(,t1 ⊕ ,t2) (check/core e t1 Γ)] [_ #f])] [`(inr ,e) (match with - [`(,t1 ⊕ ,t2) (check-core e t2 Γ)] + [`(,t1 ⊕ ,t2) (check/core e t2 Γ)] [_ #f])] [`(case ,e (,x1 ⇒ ,e1) (,x2 ⇒ ,e2)) (match (infer-core e Γ) [`(,a1 ⊕ ,a2) - (and (check-core e1 with (dict-set Γ x1 a1)) - (check-core e2 with (dict-set Γ x2 a2)))] + (and (check/core e1 with (dict-set Γ x1 a1)) + (check/core e2 with (dict-set Γ x2 a2)))] [_ #f])] ['nil @@ -111,43 +176,46 @@ [`(cons ,f1 ,f2) (match with [`(List ,t) - (and (check-core f1 t Γ) - (check-core f2 `(List ,t) Γ))] + (and (check/core f1 t Γ) + (check/core f2 `(List ,t) Γ))] [_ #f])] [`(λ (,x : ,t) ,e) (match with [`(,t1 → ,t2) - (and (equiv-type t1 t Γ) (check-core e t2 (dict-set Γ x t1)))] + (and (equiv-type t1 t Γ) (check/core e t2 (dict-set Γ x t1)))] [_ #f])] [_ (equiv-type (infer-core expr Γ) with Γ)])) -;; (infer Expr Table[Sym, Type]): Type +;; Infers a type from some expression, in a given context. (define (infer expr) (infer-core (desugar expr) #hash())) -(define (infer-core expr Γ) +(define/contract (infer-core expr Γ) + (-> stlc-ext/expr? dict? stlc-ext/type?) (match expr ['sole 'Unit] [s #:when (string? s) 'Str] [n #:when (natural? n) 'Nat] [b #:when (boolean? b) 'Bool] [x #:when (dict-has-key? Γ x) - (expand (dict-ref Γ x) Γ)] + (type->whnf (dict-ref Γ x) Γ)] + [f #:when (symbol? f) + (err (format "attempting to infer type of free variable ~a" f))] [`(type ,t1 ,t2 ,in) (infer-core in (dict-set Γ t1 t2))] [`(,e : ,t) - (if (check-core e (expand t Γ) Γ) (expand t Γ) + (if (check/core e (type->whnf t Γ) Γ) (type->whnf t Γ) (err (format "annotated expression ~a is not of annotated type ~a" e t)))] [`(inc ,e) - (if (check-core e 'Nat Γ) 'Nat + (if (check/core e 'Nat Γ) 'Nat (err (format "calling inc on incorrect type ~a" (infer-core e Γ))))] [`(if ,c ,e1 ,e2) - (if (check-core c 'Bool Γ) + (if (check/core c 'Bool Γ) (let ([t (infer-core e1 Γ)]) - (if (check-core e2 t Γ) t + (if (check/core e2 t Γ) t (err (format "condition has branches of differing types ~a and ~a" t (infer-core e2 Γ))))) (err (format "condition ~a has incorrect type ~a" c (infer-core c Γ))))] @@ -174,8 +242,8 @@ [`(case ,e (,x1 ⇒ ,e1) (,x2 ⇒ ,e2)) (match (infer-core e Γ) [`(,a1 ⊕ ,a2) - (let ([b1 (infer-core e1 (dict-set Γ x1 (expand a1 Γ)))] - [b2 (infer-core e2 (dict-set Γ x2 (expand a2 Γ)))]) + (let ([b1 (infer-core e1 (dict-set Γ x1 (type->whnf a1 Γ)))] + [b2 (infer-core e2 (dict-set Γ x2 (type->whnf a2 Γ)))]) (if (equiv-type b1 b2 Γ) b1 (err (format "case ~a is not of consistent type!" `(case (,a1 ⊕ ,a2) b1 b2)))))] [t (err (format "calling case on incorrect type ~a" t))])] @@ -183,7 +251,7 @@ ['nil (err (format "unable to infer type of empty list!"))] [`(cons ,e1 ,e2) (let ([t (infer-core e1 Γ)]) - (if (check-core e2 `(List ,t) Γ) `(List ,t) + (if (check/core e2 `(List ,t) Γ) `(List ,t) (err (format "list ~a is not of consistent type!" `(cons ,e1 ,e2)))))] [`(head ,e) (match (infer-core e Γ) @@ -195,61 +263,58 @@ [t (err (format "calling tail on incorrect type ~a" t))])] [`(λ (,x : ,t) ,e) - `(,(expand t Γ) → ,(infer-core e (dict-set Γ x t)))] + `(,(type->whnf t Γ) → ,(infer-core e (dict-set Γ x t)))] [`(,e1 ,e2) (match (infer-core e1 Γ) [`(,t1 → ,t2) - (if (check-core e2 t1 Γ) t2 + (if (check/core e2 t1 Γ) t2 (err (format "inferred argument type ~a does not match arg ~a" t1 e2)))] - [t (err (format "expected → type on application body, got ~a" t))])] - - [e (err (format "inferring an unknown expression ~a" e))])) + [t (err (format "expected → type on application body, got ~a" t))])])) ;; Expands a type alias into weak-head normal form, for literal matching. -;; (expand Type Table[Id, Expr ⊕ Type]): Type -(define (expand t Γ) +(define (type->whnf t Γ) (if (dict-has-key? Γ `(type ,t)) - (expand (dict-ref Γ `(type ,t)) Γ) t)) + (type->whnf (dict-ref Γ `(type ,t)) Γ) t)) ;; Checks if two types are equivalent up to α-conversion in context ;; (equiv-type Expr Expr Table[Sym Expr]): Bool (define (equiv-type e1 e2 Γ) - (equiv-type-core e1 e2 Γ Γ)) -(define (equiv-type-core e1 e2 Γ1 Γ2) + (equiv-type/core e1 e2 Γ Γ)) +(define (equiv-type/core e1 e2 Γ1 Γ2) (match* (e1 e2) ; bound identifiers: if a key exists in the context, look it up [(x1 x2) #:when (dict-has-key? Γ1 x1) - (equiv-type-core (dict-ref Γ1 x1) x2 Γ1 Γ2)] + (equiv-type/core (dict-ref Γ1 x1) x2 Γ1 Γ2)] [(x1 x2) #:when (dict-has-key? Γ2 x2) - (equiv-type-core x1 (dict-ref Γ2 x2) Γ1 Γ2)] + (equiv-type/core x1 (dict-ref Γ2 x2) Γ1 Γ2)] ; check for syntactic equivalence on remaining forms [(`(,l1 ...) `(,l2 ...)) - (foldl (λ (x1 x2 acc) (if (equiv-type-core x1 x2 Γ1 Γ2) acc #f)) #t l1 l2)] + (foldl (λ (x1 x2 acc) (if (equiv-type/core x1 x2 Γ1 Γ2) acc #f)) #t l1 l2)] [(v1 v2) (equal? v1 v2)])) ;; Checks if two terms are equivalent up to α-conversion in context ;; (equiv-term Expr Expr Table[Sym Expr]): Bool (define (equiv-term e1 e2 Γ) - (equiv-term-core e1 e2 Γ Γ)) -(define (equiv-term-core e1 e2 Γ1 Γ2) + (equiv-term/core e1 e2 Γ Γ)) +(define (equiv-term/core e1 e2 Γ1 Γ2) (match* (e1 e2) ; bound identifiers: if a key exists in the context, look it up [(x1 x2) #:when (dict-has-key? Γ1 x1) - (equiv-term-core (dict-ref Γ1 x1) x2 Γ1 Γ2)] + (equiv-term/core (dict-ref Γ1 x1) x2 Γ1 Γ2)] [(x1 x2) #:when (dict-has-key? Γ2 x2) - (equiv-term-core x1 (dict-ref Γ2 x2) Γ1 Γ2)] + (equiv-term/core x1 (dict-ref Γ2 x2) Γ1 Γ2)] ; function expressions: parameter names can be arbitrary [(`(λ (,x1 : ,t1) ,e1) `(λ (,x2 : ,t2) ,e2)) (let ([name gensym]) - (and (equiv-term-core e1 e2 (dict-set Γ1 x1 name) (dict-set Γ2 x2 name)) - (equiv-term-core t1 t2 Γ1 Γ2)))] + (and (equiv-term/core e1 e2 (dict-set Γ1 x1 name) (dict-set Γ2 x2 name)) + (equiv-term/core t1 t2 Γ1 Γ2)))] [(`(λ ,x1 ,e1) `(λ ,x2 ,e2)) (let ([name gensym]) - (equiv-term-core e1 e2 (dict-set Γ1 x1 name) (dict-set Γ2 x2 name)))] + (equiv-term/core e1 e2 (dict-set Γ1 x1 name) (dict-set Γ2 x2 name)))] ; check for syntactic equivalence on remaining forms [(`(,l1 ...) `(,l2 ...)) - (foldl (λ (x1 x2 acc) (if (equiv-term-core x1 x2 Γ1 Γ2) acc #f)) #t l1 l2)] + (foldl (λ (x1 x2 acc) (if (equiv-term/core x1 x2 Γ1 Γ2) acc #f)) #t l1 l2)] [(v1 v2) (equal? v1 v2)])) -- cgit v1.2.3-70-g09d2