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

Racket macro to define functions given a repeated pattern

这个问题比较难解释,因为我需要整理思绪,所以请多多包涵。出于说明目的,我已经能够将问题简化为最小示例。这个例子对于这有什么用没有任何意义,但我离题了。假设我想扩展 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.

中使用省略号感到有点困惑

可以使用 with-syntaxsyntax-case 来解决这个问题,但最简单的方法是使用 syntax-parse 的语法 classes。通过定义解析符号列表并生成单个连接标识符的语法 class,您可以将符号解析从宏主体中提取出来:

(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 ...)
         ...)]))

请注意,这在名称子句中使用了不带引号的符号,因此它会像这样工作:

(define-something
  ([a] 'whatever)
  ([b c] 'whatever2))

名称不能是求值为符号的表达式,因为信息需要在 compile-time 处已知才能用于宏扩展。由于您在评论中提到这是针对 FRP-like 系统的,因此您的信号图需要是静态的,例如 Elm 的。如果您想要构建动态信号图的能力,您将需要比宏更复杂的策略,因为该信息需要在运行时解析。