问题很难解释,因为我需要收集我的想法,所以请耐心等待。为了便于说明,我已经能够将问题简化为最小的例子。这个例子对于什么是有用的没有任何意义,但我离题了。假设我想扩展球拍语言以编写如下所示的内容:
(define-something
(['a] 'whatever)
(['b 'c] 'whatever2))
方括号之间是一个或多个符号的序列,后跟一系列球拍表达式(whatever
',这对于问题陈述并不重要)
该示例将匹配看起来像这样的宏:
(define-syntax (define-something stx)
(syntax-case stx ()
[(_ ([symb ...] body ...) ...)
#'()]))
实际上我们在这里匹配0个或更多个符号,但我们可以假设总会有至少一个符号。
在宏的主体中,我想使用连接符号作为标识符来生成函数定义。因此,对于我们愚蠢的例子,宏将扩展为:
(define (a) 'whatever)
(define (bc) 'whatever2)
我找到了一个similar question,其中海报使用预定义的字符串列表生成函数,但我不是那么流利的宏,所以我无法翻译概念来解决我的问题。我想也许我可以尝试生成一个类似的列表(通过连接符号)并应用他们的策略,但我已经越来越混淆我的宏定义中的所有省略号。我对他们在with-syntax
中使用省略号感到有点困惑。
答案 0 :(得分:4)
可以使用with-syntax
和syntax-case
解决此问题,但最简单的方法是使用syntax-parse
的语法类。通过定义解析符号列表并生成单个连接标识符的语法类,您可以解析符号解析出宏体:
(require (for-syntax syntax/parse
racket/string))
(begin-for-syntax
(define-syntax-class sym-list
#:attributes [concatenated-id]
(pattern (~and stx (sym:id ...))
#:attr concatenated-id
(let* ([syms (syntax->datum #'(sym ...))]
[strs (map symbol->string syms)]
[str (string-append* strs)]
[sym (string->symbol str)])
(datum->syntax #'stx sym #'stx #'stx)))))
现在您可以非常轻松地定义宏:
(define-syntax (define-something stx)
(syntax-parse stx
[(_ (syms:sym-list body ...) ...)
#'(begin
(define (syms.concatenated-id) body ...)
...)]))
请注意,这在name子句中使用了不带引号的符号,因此它的工作方式如下:
(define-something
([a] 'whatever)
([b c] 'whatever2))
名称不能是评估为符号的表达式,因为需要在编译时知道信息以便宏扩展可用。由于您在评论中提到这是用于类似FRP的系统,因此您的信号图将需要是静态的,例如Elm。如果您希望能够构建动态信号图,则需要比宏更复杂的策略,因为需要在运行时解析该信息。