#lang scheme/base (require "contract-arrow.ss" "contract-guts.ss" "class-internal.ss" scheme/stxparam) (require (for-syntax scheme/base)) (provide mixin-contract make-mixin-contract is-a?/c subclass?/c implementation?/c object-contract) ;; example of how one contract is constructed #; (let* ([cm (syntax-parameterize ((making-a-method #t)) (-> any/c integer? integer?))] [cf (-> integer? integer?)] [m-proj (((proj-get cm) cm) 'pos 'neg #'here "whatever")] [f-proj (((proj-get cf) cf) 'pos 'neg #'here "whatever")] [cls (make-wrapper-class 'wrapper-class '(m) (list (m-proj (λ (this x) (send (wrapper-object-wrapped this) m x)))) '(f) #f)] [o (new (class object% (field [f (λ (x) x)]) (define/public (m x) x) (super-new)))] [wo (make-object cls o (f-proj (get-field/proc 'f o)))]) ((get-field/proc 'f wo) #f)) (define-for-syntax (parse-object-contract stx args) (let loop ([args (syntax->list args)] [mtds '()] [flds '()]) (cond [(null? args) (list mtds flds)] [else (syntax-case (car args) (field) [(field id ctc) (identifier? #'id) (loop (cdr args) mtds (cons #'(id ctc) flds))] [(field . rst) (raise-syntax-error #f "malformed field specification" stx (car args))] [(id ctc) (identifier? #'id) (loop (cdr args) (cons #`(id ctc) mtds) flds)] [_ (raise-syntax-error #f "malformed object-contract clause" stx (car args))])]))) (define-struct/prop object-contract (methods method-ctcs method-wrappers fields field-ctcs) ((proj-prop (λ (ctc) (let ([meth-names (object-contract-methods ctc)] [meth-param-projs (map (λ (x) ((proj-get x) x)) (object-contract-method-ctcs ctc))] [ctc-field-names (object-contract-fields ctc)] [field-param-projs (map (λ (x) ((proj-get x) x)) (object-contract-field-ctcs ctc))]) (λ (pos-blame neg-blame src-info orig-str) (let* ([meth-projs (map (λ (x) (x pos-blame neg-blame src-info orig-str)) meth-param-projs)] [meths (map (λ (p x) (p x)) meth-projs (object-contract-method-wrappers ctc))] [cls (make-wrapper-class 'wrapper-class meth-names meths ctc-field-names #f)] [field-projs (map (λ (x) (x pos-blame neg-blame src-info orig-str)) field-param-projs)]) (λ (val) (unless (object? val) (raise-contract-error val src-info pos-blame orig-str "expected an object, got ~e" val)) (let ([objs-mtds (interface->method-names (object-interface val))] [vtable (extract-vtable val)] [method-ht (extract-method-ht val)]) (for-each (λ (m proj) (let ([index (hash-ref method-ht m #f)]) (unless index (raise-contract-error val src-info pos-blame orig-str "expected an object with method ~s" m)) ;; verify the first-order properties by apply the projection and ;; throwing the result away. Without this, the contract wrappers ;; just check the first-order properties of the wrappers, which is ;; the wrong thing. (proj (vector-ref vtable index)))) meth-names meth-projs)) (let ([fields (field-names val)]) (for-each (λ (f) (unless (memq f fields) (raise-contract-error val src-info pos-blame orig-str "expected an object with field ~s" f))) ctc-field-names)) (apply make-object cls val (map (λ (field proj) (proj (get-field/proc field val))) ctc-field-names field-projs)))))))) (name-prop (λ (ctc) `(object-contract ,@(map (λ (fld ctc) (build-compound-type-name 'field fld ctc)) (object-contract-fields ctc) (object-contract-field-ctcs ctc)) ,@(map (λ (mtd ctc) (build-compound-type-name mtd ctc)) (object-contract-methods ctc) (object-contract-method-ctcs ctc))))) (first-order-prop (λ (ctc) (λ (val) #f))) (stronger-prop (λ (this that) #f)))) (define-syntax (object-contract stx) (syntax-case stx () [(_ spec ...) (with-syntax ([(((method-id method-ctc) ...) ((field-id field-ctc) ...)) (parse-object-contract stx #'(spec ...))]) (with-syntax ([(method-name ...) (map (λ (x) (string->symbol (format "~a method" (syntax-e x)))) (syntax->list #'(method-id ...)))]) #'(build-object-contract '(method-id ...) (syntax-parameterize ((making-a-method #t)) (list (let ([method-name method-ctc]) method-name) ...)) (list (λ (this . x) (send (wrapper-object-wrapped this) method-id . x)) ...) '(field-id ...) (list field-ctc ...))))])) (define (build-object-contract methods method-ctcs wrappers fields field-ctcs) (make-object-contract methods (map (λ (x) (coerce-contract 'object-contract x)) method-ctcs) wrappers fields (map (λ (x) (coerce-contract 'object-contract x)) field-ctcs))) (define (make-mixin-contract . %/<%>s) (->d ([c% (and/c (flat-contract class?) (apply and/c (map sub/impl?/c %/<%>s)))]) () [res (subclass?/c c%)])) (define (subclass?/c %) (unless (class? %) (error 'subclass?/c "expected , given: ~e" %)) (let ([name (object-name %)]) (flat-named-contract `(subclass?/c ,(or name 'unknown%)) (lambda (x) (subclass? x %))))) (define (implementation?/c <%>) (unless (interface? <%>) (error 'implementation?/c "expected , given: ~e" <%>)) (let ([name (object-name <%>)]) (flat-named-contract `(implementation?/c ,(or name 'unknown<%>)) (lambda (x) (implementation? x <%>))))) (define (sub/impl?/c %/<%>) (cond [(interface? %/<%>) (implementation?/c %/<%>)] [(class? %/<%>) (subclass?/c %/<%>)] [else (error 'make-mixin-contract "unknown input ~e" %/<%>)])) (define (is-a?/c <%>) (unless (or (interface? <%>) (class? <%>)) (error 'is-a?/c "expected or , given: ~e" <%>)) (let ([name (object-name <%>)]) (flat-named-contract (cond [name `(is-a?/c ,name)] [(class? <%>) `(is-a?/c unknown%)] [else `(is-a?/c unknown<%>)]) (lambda (x) (is-a? x <%>))))) (define mixin-contract (->d ([c% class?]) () [res (subclass?/c c%)]))