My question is the same as the one asked here, but the answer given there doesn\'t actually work.
I have a bunch of structs inheriting from a parent struct A
Looking further into the new struct keywords that soegaard mentioned, I think I've come up with a solution that's a bit cleaner and, more importantly, much easier to abstract into a macro (those modules and requires were giving me a really hard time). I thought I'd share it for future reference. Again, this requires one of the nightly Racket builds. I'm using 6.5.0.7.
(Note: the use of case-lambda
over defining keyword arguments here is just my preference.)
#lang racket
(require (for-syntax syntax/transformer))
(struct horse (color)
#:name Horse
#:constructor-name Horse
#:transparent)
(define make-horse
(case-lambda
[() (Horse "black")]
[(color) (Horse color)]))
(define-match-expander horse
; match expander
(λ (pat)
(syntax-case pat ()
[(_ more ...) #'(Horse more ...)]))
; constructor
; edit: changing this to use make-variable-like-transformer,
; as suggested in the comments.
#;(syntax-id-rules ()
[(_ args ...) (make-horse args ...)]
[horse Horse])
(make-variable-like-transformer #'make-horse))
Sample usage:
> (define black-beauty (horse))
> black-beauty
(horse "black")
> (define ginger (horse "red"))
> ginger
(horse "red")
> (match black-beauty
[(horse color) color])
"black"
> (match ginger
[(horse color) color])
"red"
> (horse-color black-beauty)
"black"
> (horse-color ginger)
"red"
In short: instantiation, matching, and accessing fields seem to work as you would expect if you'd just used struct
. The struct id can also be used as an identifier without any syntax issues. I don't really think that part has much practical use, but I thought it was nice.
The struct
construct will define two entities with the name of the struct. A constructor and a transformer binding that has information on the struct.
In order to avoid the "duplicate identifier" error you can use #:omit-define-syntaxes
.
An alternative is to define the struct in a submodule and export only the things you need (and possibly renaming some of the identifiers).
#lang racket
(struct horse (color)
#:constructor-name make-horse
#:omit-define-syntaxes
#:transparent)
(define (horse #:color [color "black"])
(make-horse color))
(horse)
(horse #:color "red")
Output:
(horse "black")
(horse "red")
EDIT
A solution that works with match is possible with the help of a match expander.
Note: You need at least version 6.5 for this to work due the the use of #:extra-name
.
#lang racket
(module horse racket
(provide (struct-out Horse)
make-horse)
(struct horse (color)
#:extra-name Horse
#:extra-constructor-name make-horse
#:transparent))
(require 'horse)
; the custom horse constructor
(define (my-make-horse #:color [color "black"])
(make-horse color))
(define-match-expander horse
; match expander
(λ (pat)
(syntax-case pat ()
[(_horse more ...)
#'(Horse more ...)]))
; constructor
(λ (stx)
(syntax-case stx ()
[(_horse arg ...)
(syntax/loc stx
(my-make-horse arg ...))])))
(horse)
(horse #:color "red")
(match (horse #:color "blue")
[(horse color) color])
Output:
(horse "black")
(horse "red")
"blue"