[racket] struct-copy with parent type

From: Jay McCarthy (jay.mccarthy at gmail.com)
Date: Mon May 27 13:34:36 EDT 2013

The example got cut off:

#lang racket/base
(require racket/list)

(struct posn (x y) #:transparent)
(struct 3posn posn (z) #:transparent #:mutable)

(define (dynamic-duplicate s)
  (define-values (s-type skipped?) (struct-info s))
  (when (or (not s-type) skipped?)
    (error 'dynamic-duplicate
           "Can't duplicate... structure not open"))

  (define ctor (struct-type-make-constructor s-type))

  (apply ctor
         (reverse
          (let loop ([t s-type] [skipped? skipped?])
            (cond
              [(and t (not skipped?))
               (define-values (name this-count _auto
                                    this-ref mutator immutable-idxs
                                    super super-skipped?)
                 (struct-type-info t))
               (append (for/list ([i (in-range 0 this-count)])
                         (this-ref s (- (sub1 this-count) i)))
                       (loop super super-skipped?))]
              [(and (not t) (not skipped?))
               empty]
              [else
               (error 'dynamic-duplicate
                      "Can't duplicate... parent structure not open")])))))

(module+ test
  (require rackunit)
  (define p1 (3posn 1 2 3))
  (define p2 (dynamic-duplicate p1))

  (set-3posn-z! p1 4)

  (check-equal? (posn-x p1) 1)
  (check-equal? (posn-y p1) 2)
  (check-equal? (3posn-z p1) 4)

  (check-equal? (posn-x p2) 1)
  (check-equal? (posn-y p2) 2)
  (check-equal? (3posn-z p2) 3))

On Mon, May 27, 2013 at 11:34 AM, Jay McCarthy <jay.mccarthy at gmail.com> wrote:
> On Fri, May 24, 2013 at 1:37 PM, Nick Shelley <nickmshelley at gmail.com> wrote:
>> Thanks Jay.
>>
>> So with the generics approach I would just create a gen-struct-copy with the
>> same signature as struct-copy, then the implementation of gen-struct-copy in
>> each of my structs would just call struct-copy with the correct struct-id.
>> Is that the right approach?
>
> A generic can't be a macro, so really you'd have some explicit like a
> "posn-update-x" function in the generic.
>
>> Also, is there a way to introspect the struct-id if the struct is
>> transparent? Something like:
>>
>> (define p (3posn 1 2 3))
>> (struct-copy (struct-id p) p [x #:parent posn 5])
>>
>> This obviously wouldn't work if p was a posn, but in my use case, the parent
>> class is abstract so I know I'm getting child classes. I scanned through
>> some of the struct docs, but couldn't find a way to introspect a struct
>> type.
>
> It's possible to do something like what you want, but I wouldn't
> recommend it because there's no way to recover the names of the fields
> at runtime. You can just know how many there are. For example:
>
>
>>
>> On Fri, May 24, 2013 at 1:16 PM, Jay McCarthy <jay.mccarthy at gmail.com>
>> wrote:
>>>
>>> On Fri, May 24, 2013 at 11:18 AM, Nick Shelley <nickmshelley at gmail.com>
>>> wrote:
>>> > The struct-copy docs say "The result of struct-expr can be an instance
>>> > of a
>>> > sub-type of id, but the resulting copy is an immediate instance of id
>>> > (not
>>> > the sub-type)." Why is this?
>>>
>>> The technical reason this is the case is that in (struct-copy <struct>
>>> ... ...) the <struct> binding describes the fields of the structure
>>> and gives access to the constructor. Thus, in your example code, you
>>> are saying "copy this posn" and not "copy this thing and modify the
>>> posn pieces". You could imagine that we could create a link between
>>> parent and sub-structures, but it would be messy and imperative, in my
>>> mind.
>>>
>>> Alternatively, you could make a version that lists the possible
>>> children directly:
>>>
>>>
>>> https://github.com/jeapostrophe/exp/blob/cf6822f41c7637d9074638d0e9b4d4d2d7d27d7b/struct-copy-ish.rkt
>>>
>>> Alternatively, you could use generics
>>>
>>> > For instance, I would hope this would work:
>>> >
>>> > (struct posn (x y))
>>> > (struct 3d-posn posn (z))
>>> > (3d-posn-z (struct-copy posn (3d-posn 1 2 3) [x 5]))
>>> >
>>> > My actual use case is that I'm representing some data with structs. I
>>> > have
>>> > the common data in a parent struct and the specific data in the child
>>> > structs. One of the common fields is a unique id (a number I just
>>> > increment). I'd like to be able to copy a piece of data and just change
>>> > the
>>> > unique id in the struct-copy. Instead I have to have a cond or a match
>>> > that
>>> > does the same struct-copy but with different struct ids for each sub
>>> > type.
>>> >
>>> > Is there an easier way to do what I'm trying to do?
>>> >
>>> > ____________________
>>> >   Racket Users list:
>>> >   http://lists.racket-lang.org/users
>>> >
>>>
>>>
>>>
>>> --
>>> Jay McCarthy <jay at cs.byu.edu>
>>> Assistant Professor / Brigham Young University
>>> http://faculty.cs.byu.edu/~jay
>>>
>>> "The glory of God is Intelligence" - D&C 93
>>
>>
>
>
>
> --
> Jay McCarthy <jay at cs.byu.edu>
> Assistant Professor / Brigham Young University
> http://faculty.cs.byu.edu/~jay
>
> "The glory of God is Intelligence" - D&C 93



-- 
Jay McCarthy <jay at cs.byu.edu>
Assistant Professor / Brigham Young University
http://faculty.cs.byu.edu/~jay

"The glory of God is Intelligence" - D&C 93

Posted on the users mailing list.