I'm trying to learn a bit about Racket and its macro system. My introduction has been writing a thin wrapper around the IEX stock API. My prep reading included Greg Hendershott's Fear of Macros, the Racket Guide (in particular, the section on macro-generating macros and surrounding documentation), and other online resources.
I'd like to be able to do something like this:
(iex-op* chart dividends quote)
to generate the functions iex-chart
, iex-dividends
, et c. The use of a syntax transformer seemed like a natural fit to me since I don't think I could easily write a function-generating function with a clean syntax and multiple arguments.
So:
(define-syntax-rule (iex-op* op0 ...)
(begin
(iex-op op0) ...))
This assumes a working iex-op
syntax transformer:
(define iex-base-url (string->url "https://api.iextrading.com"))
(define iex-ver "1.0")
(define-syntax (iex-op stx)
(syntax-case stx (quote) ; quote is one of the operations
[(_ oper)
(with-syntax ([op-name (format-id stx "iex-~a" #'oper)])
#'(define (op-name ticker . args)
(let ([op-url
(combine-url/relative
iex-base-url
(string-join
`(,iex-ver
"stock"
,(symbol->string ticker)
,(symbol->string (syntax-e #'oper))
,(string-join args "/")) "/"))])
(string->jsexpr (http-response-body (get http-requester op-url))))))]))
My problem arises not with the iex-op
macro, which appears to do the Right Thing, but with the use of iex-op*
, which does not:
Welcome to Racket 6.3
> (enter! "iex.rkt")
> (iex-op quote)
> iex-<TAB><TAB>
iex-base-url iex-op iex-op* iex-quote iex-ver
> (iex-op* chart dividends)
> iex-<TAB><TAB>
iex-base-url iex-dividends.0 iex-op* iex-ver
iex-chart.0 iex-op iex-quote
The op*
-defined functions are suffixed with .0
. I don't know why, and I can't find documentation about it conveniently despite some hours of searching.
When I run the macro expander in DrRacket, I find that (iex-op* chart dividends)
actually does expand to
(begin (iex-op chart) (iex-op dividends))
as desired. What's worse, when I reproduce the results of the syntax transformation in the REPL, it works!
> (begin (iex-op chart) (iex-op dividends))
> iex-<TAB><TAB>
iex-base-url iex-chart.0 iex-dividends.0 iex-op* iex-ver
iex-chart iex-dividends iex-op iex-quote
What am I missing? I'll readily admit my code probably needs some substantial clean-up (I'm slowly bending my Python/C/etc. mind), but I'm rather less concerned about its aesthetic and more about what arcanum is causing this behavior.