[racket] struct equality?
Todd O'Bryan wrote:
> OK, this is harder than I thought. The struct is defined at the top
> level, so under what conditions might it be re-defined?
>
> Also, will an accessor of the form struct-name-field work with any
> struct of name struct-name, or does it only work with structs that are
> in the same scope?
The only time I know of that equivalent #:transparent structs don't
compare equal is when they're defined in different phases. If there's a
struct type called A that you require both normally and for-syntax, the
normal make-A will create a struct of a different type than the
for-syntax make-A.
It's hard to observe the difference by accident, so seeing it is
surprising. Here's a demo. In the following, module 'a makes a struct
type A and provides it. Module 'b requires it normally (phase 0) and
for-syntax (phase 1), and creates A-type values in both phases. Then an
evil macro uses syntax quasiquote/unquote to make the phase 1 value
visible in phase 0:
> (module a racket
(define-struct A (value) #:transparent)
(provide (struct-out A)))
> (module b racket
(require 'a (for-syntax 'a)) ; require 'a in phase 0 and phase 1
(define a1 (make-A 1)) ; ph-0 value, ph-0 constructor
(define-for-syntax a2 (make-A 1)) ; ph-1 value, ph-1 constructor
; get a2 into phase 0 as a quoted struct value
(define-syntax (get-a2 stx)
(syntax-case stx ()
[(_) #`#,a2]))
(define a2 (get-a2)) ; ph-0 value made by ph-1 constructor
(provide a1 a2))
> (require 'a 'b) ; require 'a and 'b in phase 0
> (equal? a1 a2)
#f
> (A-value a1) ; can use phase 0 A-value to get value from a1
1
> (A-value a2) ; CANNOT use phase 0 A-value to get value from a2
A-value: expects args of type <struct:A>; given instance of a different
<struct:A>
If it helps, you can think of the first three lines in module 'b as
(require (prefix-in phase-0- 'a)
(prefix-in phase-1- (for-syntax 'a)))
(define a1 (phase-0-make-A 1))
(define-for-syntax a2 (phase-1-make-A 1))
FWIW, I have no idea why structs are treated specially like this. It
works fine with primitive types like strings:
#lang racket
(define a1 "hello")
(define-for-syntax a2 "hello")
(define-syntax (get-a2 stx)
(syntax-case stx ()
[(_) #`#,a2]))
> (equal? a1 (get-a2))
#t
Seems it would be useful occasionally to not worry about the phase
distinction. Hope this helps!
Neil T