Does it seem weird to anybody else that there's no way to set struct
properties without mutual recursion?

Say I'm defining a 2d point struct and want to set the prop:custom-write
property to control how points print. I might start with this:

(struct point (x y)
  #:property prop:custom-write
  (lambda (this out _)
    (write-string "(point " out)
    (write (point-x this) out)
    (write-string " " out)
    (write (point-y this) out)
    (write-string ")" out)))

This works fine. But if I try to extract that lambda into a named function:

(struct point (x y) #:property prop:custom-write write-point)
(define (write-point pt out _) ...)

That doesn't work, and the error complains that I'm using write-point
before it's defined. This error makes sense to me. The property value is
associated with the type, not with instances, and the struct form is
essentially a macro that expands to a call to `make-struct-type` with a
list of property values. So I understand that I need to define those
properties before using them in the struct definition:

(define (write-point pt out _) ...)
(struct point (x y) #:property prop:custom-write write-point)

All well and good. But then I decide that defining write-foo for every one
of my struct types is tedious, so I define a make-list-type-writer helper
function and try to use it:

(define ((make-list-type-writer name . accessors) this out _)
  (write-string "(" out)
  (write-string name out)
  (for ([accessor (in-list accessors)])
    (write-string " " out)
    (write (accessor this) out))
  (write-string ")" out))

(define write-point (make-list-type-writer "point" point-x point-y))
(struct point (x y) #:property prop:custom-write write-point)

And now we've got a problem. This doesn't work because I can't pass the
point-x and point-y accessors as arguments to make-list-type-writer. They
don't exist yet! So in order to make my property value, I need the struct
defined. But in order to define the struct, I need the property value. A
workable fix is to delay evaluation somewhere with lambdas:

(define write-point
  (make-list-type-writer
    "point"
    (lambda (this) (point-x this))
    (lambda (this) (point-y this))))

This feels tedious and unnecessary. Looking at the underlying API of
make-struct-type, I can't see any way to avoid this mutual recursion. Which
is *weird* to me, because when else are you forced to use mutual recursion?
Mutually recursive functions can be transformed into functions that accept
the other function as an argument. Mutually recursive modules can (usually)
be broken down into smaller modules that don't have any cyclic
dependencies. But I can't seem to do the same for structs, which makes it
difficult to write nice helper functions for building struct types in a
first-class way.

-- 
You received this message because you are subscribed to the Google Groups 
"Racket Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to racket-users+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to