[racket] struct constructors
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 <dtp at 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