【发布时间】:2018-05-28 05:24:55
【问题描述】:
我正在尝试了解一下 Racket 及其宏系统。我的介绍是围绕IEX stock API 编写一个薄包装。我的准备读物包括 Greg Hendershott 的 Fear of Macros、Racket Guide(特别是关于宏生成宏和相关文档的部分)以及其他在线资源。
我希望能够做这样的事情:
(iex-op* chart dividends quote)
生成函数iex-chart、iex-dividends等。语法转换器的使用对我来说似乎很自然,因为我认为我不能轻松地编写一个具有干净语法和多个参数的函数生成函数。
所以:
(define-syntax-rule (iex-op* op0 ...)
(begin
(iex-op op0) ...))
假设iex-op 语法转换器工作正常:
(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))))))]))
我的问题不在于 iex-op 宏,它似乎做正确的事,而是使用 iex-op*,它没有:
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
op* 定义的函数以.0 为后缀。我不知道为什么,尽管搜索了几个小时,我还是无法方便地找到有关它的文档。
当我在 DrRacket 中运行宏扩展器时,我发现 (iex-op* chart dividends) 实际上确实扩展为
(begin (iex-op chart) (iex-op dividends))
根据需要。更糟糕的是,当我在 REPL 中重现语法转换的结果时,它可以工作!
> (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
我错过了什么?我会欣然承认我的代码可能需要一些实质性的清理(我正在慢慢地弯曲我的 Python/C/etc. 思维),但我不太关心它的美感,而更关心是什么奥秘导致了这种行为。
【问题讨论】: