Scheme has a very rich macro system that helps to reduce redundant code. PLT Scheme support two ways of writting macros (syntax-rules) and (syntax-case). I’m writting a shallow embedding of XQuery in Scheme. In order to to acomplish the end goal, I’m parsing XQuery syntax into an abstract syntax tree form that I can then maninpulate and transform into scheme code for execution.
An abstact syntax tree is just a tree of specialized nodes. Each individual node in the tree can be seen as an arrays of values, a hashs or specialized data structure. I Choose the data-type structure format. Scheme provides a (define-struct) form and Essentials of Programming Languages (EOPL) provides a (define-datatype) form, both of which define data structures. However, neither one was exactly what I was looking for.
EOPL syntax would look like the following
(define-datatype Term Term? (StringTerm string) (RationalTerm numerator denominator))
That bothered me was having to provide both the name of the datatype and the name of the datatype test predicate, Term and Term?. Convention says that the predicate function name should be the same as the datatype name with a question mark appended. Why should I have to specify both. The define-datatype macro should take care of that for me.
Back to PLT Scheme. The syntax-rules form really is just a pattern/template replacement system. The syntax-case form on the other hand allow arbitrary transformation of syntax, using transformational expressions that are executed in a seperate environment at macro expansion time before actual execution begins.
To take a symbol such as Term and expand that to two functions Term and Term?, we need the power of arbitrary expressions, not the limitations of simple pattern/template replacement. So (syntax-case) is the for we need to use. It didn’t seem that hard, so I set out to write a new (define-datatype) macro.
Well after several days of agony and failure, I finally achieve success this morning.
The trick, Quasisyntax.
Just like quoting) and quasiquoting in standard scheme expressions, you can do the same thing in macro. But when developing syntax-case macros one uses the (syntax) and (quasisyntax) forms.
In the final form below #` is the abbreviated form of (quasisyntax) and #, is the abbreviated form of (unsyntax).
The let statement is where the magic occurs to transform “Type-name” into “Type-name?”
(define-syntax (define-datatype stx)
(syntax-case stx ()
((_ Type-name (Variant-name Field-name …) …)
(let ((Type-name? (datum->syntax-object (syntax a) (string->symbol (string-append (symbol->string (syntax-object->datum (syntax Type-name))) “?”)))))
#`(begin
(define (Type-name) (display “It works\n”)
(define (#,Type-name?) (display “This works too\n)
)
