123 lines
3.7 KiB
Racket
123 lines
3.7 KiB
Racket
#lang racket/base
|
|
|
|
(require racket/set)
|
|
(require racket/match)
|
|
(require (only-in racket/class object?))
|
|
|
|
(provide ?
|
|
wildcard?
|
|
specialization?
|
|
ground?
|
|
intersect
|
|
intersect?
|
|
pattern-subst)
|
|
|
|
(struct exn:unification-failure ())
|
|
(define unification-failure (exn:unification-failure))
|
|
(define (fail) (raise unification-failure))
|
|
|
|
(struct wildcard ()
|
|
#:transparent
|
|
#:property prop:custom-write
|
|
(lambda (v port mode)
|
|
(display "?" port)))
|
|
|
|
(define ? (wildcard))
|
|
|
|
;; Any -> Boolean
|
|
;; Racket objects are structures, so we reject them explicitly for
|
|
;; now, leaving them opaque to unification.
|
|
(define (non-object-struct? x)
|
|
(and (struct? x)
|
|
(not (object? x))))
|
|
|
|
;; True iff a is a specialization (or instance) of b.
|
|
(define (specialization? a b)
|
|
(let walk ((a a) (b b))
|
|
(cond
|
|
[(wildcard? b) #t]
|
|
[(wildcard? a) #f]
|
|
[(and (pair? a) (pair? b))
|
|
(and (walk (car a) (car b)) (walk (cdr a) (cdr b)))]
|
|
[(and (vector? a) (vector? b) (= (vector-length a) (vector-length b)))
|
|
(for/and ([aa a] [bb b]) (walk aa bb))]
|
|
[(and (non-object-struct? a) (non-object-struct? b))
|
|
(walk (struct->vector a #f) (struct->vector b #f))]
|
|
[(and (hash? a) (hash? b))
|
|
(for/and ([k (in-hash-keys b)])
|
|
(and (hash-has-key? a k)
|
|
(walk (hash-ref a k) (hash-ref b k))))]
|
|
[else (equal? a b)])))
|
|
|
|
;; Any -> Boolean
|
|
;; True iff the term is completely ground, that is has no variables or
|
|
;; canonical-variables in it.
|
|
(define (ground? x)
|
|
(let walk ((x x))
|
|
(cond
|
|
[(wildcard? x) #f]
|
|
[(pair? x) (and (walk (car x)) (walk (cdr x)))]
|
|
[(vector? x) (andmap walk (vector->list x))]
|
|
[(non-object-struct? x) (walk (struct->vector x #f))]
|
|
[(hash? x) (for/and ([v (in-hash-values x)]) (walk v))]
|
|
[else #t])))
|
|
|
|
;; Vector StructType -> Struct
|
|
(define (vector->struct v t)
|
|
(apply (struct-type-make-constructor t) (cdr (vector->list v))))
|
|
|
|
;; Any Any -> Any
|
|
(define (unify a b)
|
|
(cond
|
|
[(wildcard? a) b]
|
|
[(wildcard? b) a]
|
|
[(and (pair? a) (pair? b))
|
|
(cons (unify (car a) (car b)) (unify (cdr a) (cdr b)))]
|
|
[(and (vector? a) (vector? b))
|
|
(unless (= (vector-length a) (vector-length b)) (fail))
|
|
(for/vector ((va (in-vector a)) (vb (in-vector b))) (unify va vb))]
|
|
[(and (non-object-struct? a) (non-object-struct? b))
|
|
(define-values (ta ta-skipped?) (struct-info a))
|
|
(define-values (tb tb-skipped?) (struct-info b))
|
|
(unless (eq? ta tb) (fail))
|
|
(when ta-skipped? (fail))
|
|
(when tb-skipped? (fail))
|
|
(vector->struct (unify (struct->vector a) (struct->vector b)) ta)]
|
|
[(and (hash? a) (hash? b))
|
|
(for/hash ([k (in-set (set-union (list->set (hash-keys a)) (list->set (hash-keys b))))])
|
|
(when (not (hash-has-key? a k)) (fail))
|
|
(when (not (hash-has-key? b k)) (fail))
|
|
(values k (unify (hash-ref a k) (hash-ref b k))))]
|
|
[(equal? a b)
|
|
a]
|
|
[else (fail)]))
|
|
|
|
;; Any Any (Any -> X) (-> X) -> X
|
|
(define (intersect a b ks kf)
|
|
(define-values (ok? result)
|
|
(with-handlers ([exn:unification-failure? (lambda (e) (values #f (void)))])
|
|
(values #t (unify a b))))
|
|
(if ok?
|
|
(ks result)
|
|
(kf)))
|
|
|
|
;; Any Any -> Boolean
|
|
(define (intersect? a b)
|
|
(with-handlers ([exn:unification-failure? (lambda (e) #f)])
|
|
(unify a b)
|
|
#t))
|
|
|
|
(define (pattern-subst x from to)
|
|
(let walk ((x x))
|
|
(cond
|
|
[(equal? x from) to]
|
|
[(pair? x) (cons (walk (car x)) (walk (cdr x)))]
|
|
[(vector? x) (for/vector ([e (in-vector x)]) (walk e))]
|
|
[(non-object-struct? x)
|
|
(define-values (tx tx-skipped?) (struct-info x))
|
|
(when tx-skipped?
|
|
(error 'pattern-subst "Cannot substitute in (partially-)opaque structs: ~v" x))
|
|
(vector->struct (walk (struct->vector x #f)) tx)]
|
|
[(hash? x) (for/hash ([(k v) (in-hash x)]) (values k (walk v)))]
|
|
[else x])))
|