You can have protection and subclassing like in the (running) sketch below. Something like this may work for structs. -- Matthias
#lang racket (module server racket (provide ;; exists: Class c% ;; Any -> (Instance-of c%) make-c ;; Any -> Boolean is-c%? ;; <syntax> ;; (inherit-from-c class-clause ...) ;; rewrites to a class expression whose superclass is c% ;; the sequence of class-clause may not invoke super-new inherit-from-c) (module+ test (require rackunit)) (define c% (class object% (init-field x) (super-new) (unless (integer? (sqrt x)) (error "not init with square of int: ~e" x)))) (define (make-c x) (and (number? x) (integer? x) (new c% [x (* x x)]))) (define (is-c%? x) (is-a? x c%)) (define-syntax-rule (inherit-from-c (x0) class-clause ...) ;; need to bind super-new (class c% (super-new [x (* x0 x0)]) class-clause ...)) (module+ test (check-true (is-a? (make-c 3) c%)) (check-exn exn:fail? (lambda () (new c% [x 3]))))) (module+ test (require (submod ".." server test))) (module client racket (require (submod ".." server)) (module+ test (require rackunit)) (define d% (inherit-from-c (3) )) (define d-with-double-super% (inherit-from-c (3) (super-new [x 3]))) (module+ test (check-true (is-a? (new d%) d%)) (check-true (is-c%? (new d%))) (check-exn exn:fail:object? (lambda () (new d-with-double-super%))))) (require (submod 'client test)) On May 28, 2014, at 1:25 AM, David T. Pierson <d...@mindstory.com> wrote: > On Tue, May 27, 2014 at 07:25:34PM +1200, Aidan Gauland wrote: >> What's the nearest equivalent for a struct to constructors for class >> instances? Say I have a struct with a field that should be initialised >> to a three-element vector. Right now, I'm just defining a wrapper >> make-blah. >> >> (struct blah (a b c v)) >> >> (define (make-blah) >> (blah 0 0 0 #(0 0 0)) > > I often create a wrapper like this. The wrapper can initialize fields > as in your example but it can also be used to produce side effects like > registering the instance in a container, for instance. The main > drawback with this wrapper solution is with subtyping: either you > prevent subtyping by not `provide'ing the struct id transformer binding, > or you allow subtypes but live with the fact that instances of the > subtypes will not be created through your wrapper. > > Another solution is to make the field(s) #:auto so they get a default > value automatically. The main drawback here is that there is only one > default value per struct type (defaults to #f but can be changed with > #:auto-value.) For example: > > (struct blah2 (a b c [v #:auto]) #:auto-value #(0 0 0)) > > A third possibility is to specify a #:guard procedure that verifies the > values passed to the constructor conform to whatever constraints you > wish to impose. For example: > > (struct blah3 (a b c v) > #:guard (lambda (a b c v name) > (unless (and (vector? v) (= (vector-length v) 3)) > (raise-arguments-error name "v must be a 3-element vector" > "v" v)) > (values a b c v))) > > Note that you could use the #:guard procedure to produce field values at > instantiation time without using a wrapper function, but the drawback is > that it can only do so for fields which are *not* #:auto. Therefore the > calling code must still provide values when calling the constructor, > even if they get ignored by the #:guard procedure. > > David > > ____________________ > Racket Users list: > http://lists.racket-lang.org/users ____________________ Racket Users list: http://lists.racket-lang.org/users