[racket] struct constructors

From: Matthias Felleisen (matthias at ccs.neu.edu)
Date: Wed May 28 10:26:01 EDT 2014

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



Posted on the users mailing list.