I'm doing some experiments with structures. I'm trying to write a macro that lets me create structures that support runtime, dictionary lookup. I've started with:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #lang racket (require (for-syntax racket/syntax)) (define-syntax (def-struct stx) (syntax-case stx () [(_ name (id ...)) (with-syntax ([(id-ref ...) (map (lambda (id) (format-id #'stx "~a-~a" #'name (syntax-e id))) (syntax->list #'(id ...)))]) (quasisyntax/loc stx (begin (define-struct name (id ...) #:property prop:procedure (lambda (a-struct field-name) (cond [(eq? field-name 'id) (id-ref a-struct)] ... [else (error 'name "Unknown field ~a" field-name)]))))))])) ;; Example usage (def-struct person (name age)) (define p (person 'bob 32)) (p 'name) (p 'age) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; This sorta works, although it may have some hygiene issues. I also feel awkward about the use of the id-refs in the macro above because it's mixing internal and external API usages. I already know that, at the point of the procedure, that the value is of the right struct type. The public-facing accessors, though, make no assumptions. I want to get at the internal interface of the structure, the values provided by make-struct-type, within the definition of a struct. I can throw caution to the wind and do the following: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #lang racket (require racket/unsafe/ops) (define-syntax (def-struct stx) (syntax-case stx () [(_ name (id ...)) (syntax/loc stx (define-struct name (id ...) #:property prop:procedure (lambda (a-struct field-name) (cond [(eq? field-name 'id) (unsafe-struct-ref a-struct (struct-field-index id))] ... [else (error 'name "Unknown field ~a" field-name)]))))])) (def-struct person (name age)) (define p (person 'bob 32)) (p 'name) (p 'age) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; where I use struct-field-index to figure out how to index the structure. This feels better than what I had earlier, with less syntax tricks, though this too makes me feel a little dirty. I don't want to touch racket/unsafe/ops unless I really have to. struct-field-index is a syntax parameter that exists as a form to help writers of prop:procedure procedures. But as far as I can tell, its use is limited to passing a number directly to prop:procedure. What I don't see are equivalent simple syntactic forms to let me get at the internal constructor, predicate, accessor, and mutator functions from within a struct definition, and I think that's what's missing. If the internal make-, ?, -ref, and -set! bindings were similarly exposed as syntax parameters for the syntactic context of a struct definition, then that would address this API awkwardness. Here's what the change looks like: https://github.com/dyoo/racket/commit/38c41b34f22c60d057bcbaa4784a87bf768ee2cf This adds syntax-parameters struct-field-ref and struct-field-set!. Then the definition I had earlier changes to this: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #lang racket (define-syntax (def-struct stx) (syntax-case stx () [(_ name (id ...)) (syntax/loc stx (define-struct name (id ...) #:property prop:procedure (lambda (a-struct field-name) (cond [(eq? field-name 'id) (struct-field-ref a-struct (struct-field-index id))] ... [else (error 'name "Unknown field ~a" field-name)]))))])) (def-struct person (name age)) (define p (person 'bob 32)) (p 'name) (p 'age) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; The change allows me to reuse define-struct more readily, without having to go through make-struct-type, unhygienic syntax, or unsafe operations. Is exposing the internal -ref, -set!, make- and predicate bindings within a define-struct's body worthwhile to other people? _________________________________________________ For list-related administrative tasks: http://lists.racket-lang.org/listinfo/dev