在重复模式下定义函数的球拍宏

时间:2016-01-13 20:35:49

标签: scheme racket

问题很难解释,因为我需要收集我的想法,所以请耐心等待。为了便于说明,我已经能够将问题简化为最小的例子。这个例子对于什么是有用的没有任何意义,但我离题了。假设我想扩展球拍语言以编写如下所示的内容:

(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中使用省略号感到有点困惑。

1 个答案:

答案 0 :(得分:4)

可以使用with-syntaxsyntax-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。如果您希望能够构建动态信号图,则需要比宏更复杂的策略,因为需要在运行时解析该信息。