[racket] struct-copy and custom-write bug?
This is not a problem with copy-struct. It's a weird combination of small bugs.
One of the problems is that the result of (struct-copy Base (Atom
3))) is a Base? struct, not a Atom? struct. More on this later.
First, let's analyze a reduced version of your program. Just delete
the copy-struct and all the Atom related parts.
#lang racket
(define (Print stx port mode)
(write-string (Pair-cdr stx) port))
(struct Base ()
#:methods gen:custom-write
[(define write-proc Print)])
(struct Pair Base (car cdr))
(Pair-cdr (Base)) ;==> doesn't finish
The problem here is that (Pair-cdr (Base)) tries to raise an error,
because it's not a Pair. But to be helpful, the error tries to print
(Base), but Base has a gen:custom-write procedure that is Print. The
problem is that Print calls (Pair-cdr stx) [with stx = (Base)] that
raise an error ... So this never finish.
The first fix is in the Print procedure, to make it print plain Base struct's
(define (Print-Everything stx port mode)
(cond
[(Atom? stx) (write-string "ATOM " port)]
[(Pair? stx) (Print-Everything (Pair-cdr stx) port mode)] ; and Pair-car ?
[(Base? stx) (write-string "BASE-OR-UNKNOWN " port)]
[else (error "not a BASE :(")])) ; TODO: improve error message
(I changed the name. I think that instead of a big cond, it's better
to give each derived struct its own gen:custom-write procedure.)
The other problem is that struct-copy doesn't really copy all the
details of the derived struct. It's more like struct-copy/cast-to. For
example, the result of (struct-copy Base (Atom 3))) is a Base?
struct, not a Atom? struct.
AFAIK it's not possible to clone an arbitrary struct. (It's possible
in some cases like #:prefab or #:transparent struct's.
http://rosettacode.org/wiki/Polymorphic_copy#Racket)
One possible solution is to create a custom gen:custom-copy
(require racket/generic)
(define-generics custom-copy
(struct-copy-x custom-copy)) ;TODO: add wellknown struct's
(define (Copy-Everything stx)
(cond
[(Atom? stx) (Atom (Atom-datum stx))]
[(Pair? stx) (Pair (Pair-car stx) (Pair-cdr stx))]
[(Base? stx) (Base)]
[else (error "not a BASE :(")])) ; TODO: improve error message
(Also, instead of a big cond, it's better to give each derived struct
its own gen:custom-copy.)
And now the example is
(struct Base ()
#:methods gen:custom-write
[(define write-proc Print-Everything)]
#:methods gen:custom-copy
[(define struct-copy-x Copy-Everything)])
(struct Atom Base (datum))
(struct Pair Base (car cdr))
(define t (struct-copy-x (Atom 3)))
t
;---
Gustavo
On Sat, Mar 1, 2014 at 7:50 AM, Jon Stenerson <jonstenerson at comcast.net> wrote:
> When I put the following in the DrRacket definitions window and then
> evaluate t, it works for a few seconds and runs out of memory. Anyone
> understand the problem?
>
> #lang racket
>
> (define (Print stx port mode)
> (if (Atom? stx)
> (write-string "ATOM " port)
> (Print (Pair-cdr stx) port mode)))
>
> (struct Base ()
> #:methods gen:custom-write
> [(define write-proc Print)])
>
> (struct Atom Base (datum))
> (struct Pair Base (car cdr))
>
> (define t (struct-copy Base (Atom 3)))
> ____________________
> Racket Users list:
> http://lists.racket-lang.org/users