1 ;;;; Copyright (C) 2024 Vouivre Digital Corporation
3 ;;;; This file is part of Vouivre.
5 ;;;; Vouivre is free software: you can redistribute it and/or
6 ;;;; modify it under the terms of the GNU General Public
7 ;;;; License as published by the Free Software Foundation, either
8 ;;;; version 3 of the License, or (at your option) any later version.
10 ;;;; Vouivre is distributed in the hope that it will be useful,
11 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ;;;; General Public License for more details.
15 ;;;; You should have received a copy of the GNU General Public
16 ;;;; License along with Vouivre. If not, see <https://www.gnu.org/licenses/>.
18 (define-module (vouivre type system)
19 #:use-module (ice-9 match)
20 #:use-module (ice-9 receive)
21 #:use-module (srfi srfi-1)
22 #:use-module (srfi srfi-9)
23 #:use-module (vouivre misc)
32 define-derivation-list
36 ;;;; miscellaneous routines
38 (define (papply proc . args)
40 (apply proc (append args rest))))
42 (define-syntax-rule (second-value expr)
54 (map cons (car lst) acc))))))
58 (define *constructors* (make-hash-table))
59 (define *rules* (make-hash-table))
60 (define *definition-rules* (make-hash-table))
61 (define *types* (make-hash-table))
62 (define *ctxs* (make-parameter #f))
64 (define-record-type derivation
65 (make-derivation tree steps)
67 (tree derivation-tree)
68 (steps derivation-steps))
70 (define-record-type substitution
71 (make-substitution pattern source destination)
73 (pattern substitution-pattern)
74 (source substitution-source)
75 (destination substitution-destination))
77 (define element-of-:xX second)
78 (define judgment-context (papply take-while (compose not (papply eq? '⊢))))
79 (define judgment-content last)
80 (define meta-conclusion car)
81 (define meta-hypotheses cdr)
82 (define constructor-ref (papply hash-ref *constructors*))
83 (define (context-ref ctx var)
85 (eq? var (element-of-:xX :xX)))
87 (define rule-ref (papply hash-ref *rules*))
88 (define type-ref (papply hash-ref *types*))
90 ;; NOTE: The requirement that for declared types and constructors we can list
91 ;; the kind of its arguments, if any, imposes additional constraints that
92 ;; must be checked in `define-rule'. Incidentally, we can dispense with
93 ;; some checks here because we know that interned metas satisfy these
95 (define (meta-arguments-kinds meta)
96 (let ((content (judgment-content (meta-conclusion meta))))
99 "meta-arguments-kinds: requires a type kind of judgment.")
100 (case (first content)
102 (case (car (second content))
115 (else (parse-error "unsupported type."))))
118 "meta-arguments-kinds: requires a type kind of judgment."))))))
121 (make-derivation (cons judgment #f) '()))
123 (define (derivation-point derivation)
126 (let rec ((tree (derivation-tree derivation)))
129 (ifn (list? (cdr tree))
135 (define (expand-derivation-point derivation step stuff)
136 (match-let (((expanded? . tree)
137 (let rec ((tree (derivation-tree derivation)))
139 (cons #t (cons (car tree) stuff))
140 (ifn (list? (cdr tree))
142 (match-let (((expanded? . rlst)
143 (fold (lambda (x prev)
145 (cons #t (cons x (cdr prev)))
148 (cons (cdr r) (cdr prev))))))
151 (cons expanded? (cons (car tree) (reverse rlst)))))))))
152 (values (make-derivation tree (cons step (derivation-steps derivation)))
155 (define (list-hypotheses derivation)
157 (let rec ((tree (derivation-tree derivation))
159 (if (or (not (cdr tree))
160 (not (list? (cdr tree))))
161 (cons (car tree) lst)
162 (fold rec lst (cdr tree))))
165 ;;;; parsing routines
167 ;;; In what follows, we want to do for judgments what `map' and `fold' do for
168 ;;; lists – to evaluate a function on the judgment's intrinsics. We proceed by
169 ;;; implementing map/fold routines in a "Russian doll" kind of way from big to
170 ;;; small (although they appear in this file in reverse order i.e from callee to
171 ;;; caller): judgment, content and context, type, and element. For each of these
172 ;;; we have a "dispatch" routine to abstract away the structural analysis and
173 ;;; validation that is common to the corresponding map and fold. Follows, a
174 ;;; simple application of `judgment-map'. The others work in a similar fashion.
176 ;;; (define (prime x) (string->symbol (string-append (symbol->string x) "'")))
178 ;;; (judgment-map (lambda (kind x y)
186 ;;; => (Λ ⊢ (type B'))
188 ;;; As you can see, there are two differences with `map'. The first is `kind',
189 ;;; which binds to the kind of intrinsics currently being mapped (either
190 ;;; 'element, 'type, 'Γ, or 'Δ). The second is the range, `#f' here, which
191 ;;; allows the split of variable declarations between Γs and Δs.
193 (define (parse-error str . format-args)
194 (error (string-append "parsing error: "
195 (apply format #f str format-args))))
199 (define (pre-destruct kind expr)
200 (define fmt "destruct: unknown expression ~a of kind ~a.")
201 ;; Since the name can be any expression including `#f' we wrap it as a
202 ;; "Maybe" so that we can distinguish the absence of a name from the name
205 (values (cons 'just expr) '())
208 (parse-error fmt expr kind)
209 (if (list? (car expr))
211 (parse-error fmt expr kind)
212 ;; When instead of a name we have an s-expression the
213 ;; expression must have a size of exactly two.
214 (ifn (null? (cddr expr))
215 (parse-error fmt expr kind)
216 (values #f (cdr expr))))
217 (values (cons 'just (car expr))
220 (define (destruct kind expr)
221 "Destructure an element or type into its head, tail, and meta data, raising an
222 error if the expression has an unexpected format.
224 kind: The kind of expression to destructure. This is either 'type or
227 expr: The expression to destructure.
229 Returns Maybe name, List arguments, and either `#f' or the name's meta data,
231 (define referee (if (eq? kind 'element) constructor-ref type-ref))
232 (define fmt "destruct: unknown expression ~a of kind ~a.")
233 (receive (maybe-name args) (pre-destruct kind expr)
235 (values maybe-name args #f)
236 (if-let (meta (referee (just maybe-name)))
237 (let ((n-args (length args))
238 (n-meta-args (length (second-value
243 (meta-conclusion meta))))))))
244 (if (= n-args n-meta-args)
245 (values maybe-name args meta)
246 ;; We allow constructors declared with no arguments to be
247 ;; curried applications.
248 (if (and (eq? kind 'element)
251 (values maybe-name args meta)
252 (parse-error "destruct: declared expression ~a has ~a arguments, but expected ~a." expr n-args n-meta-args))))
253 (values maybe-name args #f)))))
255 (define (element-dispatch function exprs)
256 "Dispatch a function based on the given elements or raise an error if they
257 have an unexpcted format. There are three kinds of expressions: declared
258 constructors, undeclared constructors, and curried application to an
261 function: A function of one argument called with 'declared, 'undeclared, or
262 'applied if, respectively, expressions are: all declared, any are
263 undeclared, or all are applied.
265 exprs: List of expressions.
267 Returns the result of the function application."
268 (define element-destruct (papply destruct 'element))
269 (receive (maybe-names argss metas)
270 (unzip3 (map (lambda (expr) (receive vals (element-destruct expr) vals))
272 ;; All declared constructors must have the same meta data and their number
273 ;; of arguments must match.
274 (unless (apply eq? (remove not metas))
275 (parse-error "element-dispatch: declared constructors don't match."))
276 (if (every (lambda (maybe-name args meta)
280 (zero? (length (second-value
285 (meta-conclusion meta))))))))))
286 maybe-names argss metas)
287 (function 'applied maybe-names argss metas)
288 (if (every identity metas)
289 (function 'declared maybe-names argss metas)
290 (function 'undeclared maybe-names argss metas)))))
292 (define (element-map function expr . more)
293 "Map a function on the given elements or raise an error if they have unexpected
296 function: A function that will always be called with 'element as its first
297 argument, to be consistant with `type-map', and then with the
298 corresponding intrinsics of each expression.
300 expr . more: One or more expressions.
302 Returns an element that is the result of mapping the function to the
303 corresponding intrinsics."
304 (let ((exprs (cons expr more)))
306 (lambda (kind maybe-names argss metas)
309 (if (null? (car argss))
310 (apply function 'element exprs)
311 (cons (just (car maybe-names)) ; all expressions have the same name
314 (apply element-map function formals))
317 (apply function 'element exprs))
319 ;; Each expression is of the form '(f x).
320 (list (apply element-map function (map first exprs))
321 (apply element-map function (map second exprs))))))
324 (define (element-fold function init expr . more)
325 "Fold a function over the given elements or raise an error if they have
328 function: A function that will always be called with 'element as its first
329 argument, to be consistant with `type-fold', and then with the
330 corresponding intrinsics of each expression, followed by the
333 expr . more: One or more expressions.
335 Returns an element that is the result of folding the function over the
336 corresponding intrinsics."
337 (let ((exprs (cons expr more)))
339 (lambda (kind maybe-names argss metas)
342 (if (null? (car argss))
343 (apply function 'element `(,@exprs ,init))
346 (apply element-fold function
351 (apply function 'element `(,@exprs ,init)))
353 (apply element-fold function
354 (apply element-fold function init
356 (map second exprs)))))
359 (define (type-dispatch function exprs)
360 "Dispatch a function based on the given types or raise an error if they
361 have an unexpcted format. There are two kinds of expressions: declared
362 types, and undeclared types.
364 function: A function of one argument called with 'declared or 'undeclared if,
365 respectively, expressions are: all declared, or any are undeclared.
367 exprs: List of expressions.
369 Returns the result of the function application."
370 (define type-destruct (papply destruct 'type))
371 (receive (maybe-names argss metas)
372 (unzip3 (map (lambda (expr) (receive vals (type-destruct expr) vals)) exprs))
373 ;; All declared types must have the same meta data and their number
374 ;; of arguments must match.
375 (unless (apply eq? (remove not metas))
376 (parse-error "type-dispatch: declared types don't match."))
377 (if (every identity metas)
378 (function 'declared maybe-names argss metas)
379 (function 'undeclared maybe-names argss metas))))
381 (define (type-map function expr . more)
382 "Map a function to type-expressions. See the documentation of `element-map' which
383 has analogous behaviour except for the function who's called on a kind that
384 alternates between 'type and 'element based on the intrinsic."
385 (let ((exprs (cons expr more)))
387 (lambda (kind maybe-names argss metas)
390 (if (null? (car argss))
391 (apply function 'type exprs)
392 (cons (just (car maybe-names)) ; all expressions have the same name
395 (if (eq? 'binding (car formals))
399 (apply element-map function xs)
400 (apply type-map function Xs)))
402 (parse-error "type-map: declared types don't match.")))
403 (apply (case (car formals)
408 function (cdr formals))))
409 (cons (meta-arguments-kinds (type-ref (car expr)))
412 (apply function 'type exprs))))
415 (define (type-fold function init expr . more)
416 "Fold a function over type-expressions. See the documentation of `element-fold'
417 which has analogous behaviour except for the function who's called on a kind
418 that alternates between 'type and 'element based on the intrinsic."
419 (let ((exprs (cons expr more)))
421 (lambda (kind maybe-names argss metas)
424 (if (null? (car argss))
425 (apply function 'type `(,@exprs ,init))
428 (if (eq? 'binding (car formals))
429 (match (but-last (cdr formals))
431 (apply element-fold function
432 (apply type-fold function
437 (parse-error "type-fold: declared types don't match.")))
438 (apply (case (car formals)
445 (but-last (cdr formals)))))
446 init (cons (meta-arguments-kinds (type-ref (car expr)))
449 (apply function 'type `(,@exprs ,init)))))
452 (define (split-in-three lst start end)
453 "Split the given list in three with `start' and `end' the indices delimiting
454 the resulting middle list e.g. (a b c d e) 2 3 => (a b) (c) (d e)."
473 (define (context-dispatch function ranges exprs)
474 (define (expand-range range start end)
477 (list (or (first range) start)
478 (or (second range) end))))
480 (receive (Γs :xXss Δs)
482 (map (lambda (range expr)
484 (((? symbol? Γ) :xXs ... (? symbol? Δ))
486 (apply split-in-three
488 (expand-range range 0 (length :xXs)))
489 (list (cons Γ a) b `(,@c ,Δ))))
490 (((? symbol? Γ) :xXs ... ('/ (? symbol? Δ) x y))
492 (apply split-in-three
494 (expand-range range 0 (length :xXs)))
495 (list (cons Γ a) b `(,@c ,Δ))))
496 (((? symbol? Γ) :xXs ...)
498 (apply split-in-three
500 (expand-range range 0 (length :xXs)))
501 (list (cons Γ a) b c)))
504 (apply split-in-three
506 (expand-range range 0 (length :xXs)))
508 (_ (parse-error "context-fold: unknown expression."))))
509 (or ranges (list-tabulate (length exprs) not))
511 (if (apply = (map length :xXss))
513 (parse-error "context-dispatch: contexts should have the same range, but got ~{~a~^ ~}." :xXss)))))
515 (define (context-map function ranges expr . more)
517 (lambda (Γs :xXss Δs)
519 (if (null? (car Γs)) '() (apply function 'Γ Γs))
524 (let* ((:xXs (but-last formals)) ; one decl per judgment
528 (if (any identity (map (lambda (ctx :xX)
529 (context-ref ctx (second :xX)))
531 (parse-error "context-map: variable can only be declared once.")
532 (cons (map cons :xXs ctxs)
535 (apply function 'variable (map second :xXs))
536 (apply type-map function (map third :xXs)))
538 (cons (list-tabulate (length :xXss) (const '()))
541 (if (null? (car Δs)) '() (apply function 'Δ Δs))))
542 ranges (cons expr more)))
544 (define (context-fold function ranges init expr . more)
546 (lambda (Γs :xXss Δs)
547 ;; See `context-map' for the format of Γs :xXss Δs.
549 (ifn (null? (car Δs)) function (lambda formals (last formals)))
554 (let* ((:xXs (but-last formals)) ; one decl per judgment
555 (prev (last formals)))
557 (map (lambda (ctx :xX)
558 (context-ref ctx (element-of-:xX :xX)))
561 (parse-error "context-fold: variable can only be declared once.")
563 (*ctxs* (map cons :xXs (*ctxs*)))
564 (apply function 'variable
565 `(,@(map element-of-:xX :xXs)
566 ,(apply function 'type `(,@(map third :xXs)
568 (apply (ifn (null? (car Γs)) function (lambda formals (last formals)))
571 ranges (cons expr more)))
573 (define (content-dispatch function exprs)
574 (define (content-match expr)
577 (('/ (? symbol? J) (? symbol? a) (? symbol? b))
578 (list 'generic (make-substitution J a b)))
584 ;; judgmentally equal types
592 (make-substitution A x y)
593 (make-substitution B x' y)))
596 ;; judgmentally equal elements of a type
607 (make-substitution a x y)
608 (make-substitution b x' y)
609 (make-substitution A x y)))
611 (list 'element-eq a b A))
615 (_ (parse-error "content-dispatch: unknown expression."))))
616 (let* ((xs (map content-match exprs))
617 (generic? (papply eq? 'generic))
618 (kinds (map car xs)))
619 (unless (apply eq? (remove generic? kinds))
620 (parse-error "content-dispatch: contents don't match."))
621 (ifn (any generic? kinds)
622 (apply function (find (compose not generic?)
624 (unzip (map cdr xs)))
625 (apply function 'generic
626 (map (lambda (kind x)
640 (define (content-map function expr . more)
642 (lambda (kind . args)
645 (apply function 'generic args))
647 (list 'type (apply type-map function (first args))))
650 (apply element-map function (first args))
651 (apply type-map function (second args))))
654 (apply type-map function (first args))
655 (apply type-map function (second args))))
658 (apply element-map function (first args))
659 (apply element-map function (second args))
660 (apply type-map function (third args))))))
663 (define (content-fold function init expr . more)
665 (lambda (kind . args)
668 (apply function 'generic `(,@args ,init)))
670 (apply type-fold function init (first args)))
672 (apply element-fold function
673 (apply type-fold function init (second args))
676 (apply type-fold function
677 (apply type-fold function init (second args))
680 (apply element-fold function
681 (apply element-fold function
682 (apply type-fold function init (third args))
687 (define (judgment-dispatch function exprs)
689 (((contexts ... '⊢ contents) ...)
690 (function contexts contents))
691 (_ (parse-error "judgment-dispatch: unknown expression."))))
693 (define (judgment-map function ranges expr . more)
694 (judgment-dispatch (lambda (ctxs cnts)
695 (append (apply context-map function ranges ctxs)
697 (list (apply content-map function cnts))))
700 (define (judgment-fold function ranges init expr . more)
701 (parameterize ((*ctxs* (map (const '()) (cons expr more))))
702 (judgment-dispatch (lambda (ctxs cnts)
703 (apply content-fold function
704 (apply context-fold function ranges init ctxs)
708 (defmacro define-rule (name judgment . more)
710 (when (eq? name 'exact)
711 (parse-error "define-rule: the rule name \"exact\" is reserved."))
712 (for-each (lambda (judgment)
714 (lambda (kind x prev)
717 (cons judgment more))
718 ;; We declare types or constructors when they occur.
719 (let ((content (judgment-content judgment)))
720 (when (list? content)
721 (let ((kind (case (first content)
725 (when (member kind '(type element))
726 (if-let (maybe-name (pre-destruct kind (second content)))
727 (unless (and (eq? 'element kind)
729 (lambda (kind' x prev)
730 (ifn (eq? kind kind')
732 (or prev (context-ref (car (*ctxs*))
733 (just maybe-name)))))
735 (unless (any (lambda (judgment)
737 (lambda (kind' x prev)
738 (ifn (eq? kind kind')
740 (or prev (equal? x (just maybe-name)))))
743 (hash-set! (if (eq? kind 'type)
747 (cons judgment more)))))))))
748 (hash-set! *rules* name `,(cons judgment more))
750 (hash-set! *rules* ',name ',(cons judgment more))
753 (define (replace-judgment mappings judgment)
757 (if (member kind '(Γ Δ))
758 (assoc-ref mappings (car x))
759 (if (member kind '(element variable type generic))
760 (assoc-ref mappings x)
762 (parse-error "replace-judgment: expression ~a not mapped in ~a." x mappings)))
765 (define (substitute mapper x y mapping)
766 (acons (substitution-pattern x)
769 (if (and (eq? kind 'type)
770 (receive (maybe-name args meta) (destruct 'type z)
771 (and (not meta) (= 1 (length args)))))
772 (ifn (equal? (second z) (assoc-ref mapping (substitution-source x)))
775 (assoc-ref mapping (substitution-destination x))))
776 (ifn (equal? z (assoc-ref mapping (substitution-source x)))
778 (assoc-ref mapping (substitution-destination x)))))
782 (define* (apply-rule derivation name #:key start end . args)
783 (let ((err1 "apply-rule: derivation is pointless.")
784 (err2 "apply-rule: substitutions are only allowed in rules.")
785 (err3 "apply-rule: can't apply a specific judgment rule to a generic judgment expression.")
786 (err4 "define-derivation: rule ~s is undefined."))
787 (expand-derivation-point
791 `(,name #:start ,start #:end ,end ,@args)
792 `(,name #:start ,start ,@args))
794 (let ((Jp (derivation-point derivation)))
797 (if (eq? name 'exact)
799 (if-let (m (ifn (eq? name 'definition)
801 (hash-ref *definition-rules* (just (pre-destruct 'type (second (judgment-content Jp)))))))
802 ;; NOTE: Computing the mapping is essential to be sure
803 ;; the meta-conclusion matches the judgment point even
804 ;; in cases where there are no hypotheses.
807 (lambda (kind x y prev)
810 (if (substitution? y)
812 (ifn (substitution? x)
813 (acons (car x) y prev)
816 (acons (substitution-pattern x)
819 (if (substitution? y)
821 (ifn (substitution? x)
822 (acons (car x) y prev)
825 (acons (substitution-pattern x)
828 (if (substitution? y)
830 (ifn (substitution? x)
832 (substitute element-map x y prev))))
834 (if (substitution? y)
836 (ifn (substitution? x)
838 (substitute type-map x y prev))))
840 (if (and (not (list? y))
842 ;; if y is generic so must x.
844 (ifn (substitution? x)
847 (acons (substitution-pattern x)
849 (substitute content-map x y prev)))))
854 (list #f (list start end))
855 ;; When `#:start'/`#:end' is provided the evaluator
856 ;; adds it to the head of the `args' list (see the
857 ;; documentation for `define*') followed by its value,
858 ;; and must be removed.
872 (cons (replace-judgment mapping judgment)
874 (meta-hypotheses m)))
875 (parse-error err4 name)))))))
877 (defmacro define-derivation% (name kind maybe-assignment conclusion hypotheses rule . more)
879 (fold (lambda (step derivation)
880 (apply apply-rule derivation step))
883 (ifn (lset= equal? hypotheses (list-hypotheses derivation))
885 "define-derivation: expected remaining hypotheses ~a, but got ~a."
886 hypotheses (list-hypotheses derivation))
892 (cons `(,@(judgment-context conclusion)
894 ,(if (eq? kind 'type)
895 (list 'type (just maybe-assignment))
897 (just maybe-assignment)
898 (third (judgment-content conclusion)))))
903 (cons `(,@(judgment-context conclusion)
905 ,(if (eq? kind 'type)
907 (just maybe-assignment)
908 (second (judgment-content conclusion)))
910 (just maybe-assignment)
911 (second (judgment-content conclusion))
912 (third (judgment-content conclusion)))))
914 (hash-set! (if (eq? kind 'type) *types* *constructors*)
915 (just (pre-destruct 'type (just maybe-assignment)))
920 (hash-set! *rules* name (cons conclusion hypotheses))
923 ;; NOTE: A derivation must have at least one derivation rule (can't derive a
924 ;; statement out of nothing).
925 (define-syntax define-derivation
926 (syntax-rules (:= ⊢ type :)
927 ((_ name ((Γ ⊢ (:= a (type expr)))
932 (define-derivation% name type (just . a) (Γ ⊢ (type expr)) (hypothesis ...)
936 ((_ name ((Γ ⊢ (:= a (: b B)))
941 (define-derivation% name element (just . a) (Γ ⊢ (: b B)) (hypothesis ...)
945 ((_ name (conclusion hypothesis ...)
949 (define-derivation% name #f #f conclusion (hypothesis ...)
954 (define (define-derivation-list name derivation)
955 (let ((tree (derivation-tree derivation)))
956 `(define-derivation ,name
957 (,(car tree) ,@(list-hypotheses derivation))
958 ,@(reverse (derivation-steps derivation)))))