I think the issue is that there's genuinely a cycle here, and so you have
to take advantage of mutation or the implicit mutation of module
initialization. point-x has a reference to the point structure type, which
in turn has a reference to the table of properties, which contains the
writer function, which refers to point-x.

Sam

On Fri, Feb 22, 2019, 5:35 PM Jack Firth <jackhfi...@gmail.com> wrote:

> 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.
>

-- 
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