在给定重复模式的情况下定义函数的球拍宏
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-syntax
和 syntax-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 的。如果您想要构建动态信号图的能力,您将需要比宏更复杂的策略,因为该信息需要在运行时解析。
这个问题比较难解释,因为我需要整理思绪,所以请多多包涵。出于说明目的,我已经能够将问题简化为最小示例。这个例子对于这有什么用没有任何意义,但我离题了。假设我想扩展 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-syntax
和 syntax-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 的。如果您想要构建动态信号图的能力,您将需要比宏更复杂的策略,因为该信息需要在运行时解析。