如何使用 Racket 宏定义函数?

发布于 2024-09-02 12:51:36 字数 289 浏览 7 评论 0原文

我正在尝试编写一个宏来定义具有相关函数的特殊数据结构类。

我知道这是可能的;它在核心语言本身中完成了多次。

作为一个具体的例子,我将如何在Scheme本身中定义define-struct宏。它需要创建make-structstruct-<>等函数。

我尝试使用 define 执行此操作,但是,这仅定义了宏的词法范围内的函数。

如何在宏中实际定义函数?

I am trying to write a macro that defines a special class of data structure with associated functions.

I know this is possible; it is done multiple times in the core language itself.

As a specific example, how would I define the define-struct macro in Scheme itself. It needs to create make-struct, struct-<<field>>, etc functions.

I tried doing this using define, however, this only defines the function in the macro's lexical scope.

How can I actually define a function in a macro?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦途 2024-09-09 12:51:36

答案的关键是datum->syntax。基本思想是,您想要获取一些随机数据并将其转换为语法 - 在本例中,将符号转换为标识符。标识符基本上是一个带有一些词汇信息的符号,这些信息(非常粗略地)指示它是如何绑定的。使用datum->syntax你可以做到这一点:它需要一个现有的语法片段,它是从中复制绑定的地方,以及一个数据(这里是一个符号),它是包含的值在语法包装器中。

下面是一个示例,演示了如何使用类似 define-struct 的工具:

#lang scheme
;; implements a defstruct-like macro that uses association lists
(define-syntax (defstruct-lite stx)
  (syntax-case stx ()
    [(defstruct-lite name field ...)
     (let ([make-id
            (lambda (template . ids)
              (let ([str (apply format template (map syntax->datum ids))])
                (datum->syntax stx (string->symbol str))))])
       (with-syntax ([make-name (make-id "make-~a" #'name)]
                     [name?     (make-id "~a?" #'name)]
                     [(arg ...) (generate-temporaries #'(field ...))]
                     [(name-field ...)
                      (map (lambda (f) (make-id "~a-~a" #'name f))
                           (syntax->list #'(field ...)))])
         #'(begin
             (define (make-name arg ...) (list 'name (cons 'field arg) ...))
             (define (name? x) (and (pair? x) (eq? 'name (car x))))
             (define (name-field x)
               (and (name? x) (cdr (assq 'field (cdr x)))))
             ...)))]))

下面是一个使用它的示例:

(defstruct-lite point x y)
(point-y (make-point 1 2))

The key for an answer is datum->syntax. The basic idea is that you want to take some random data and turn it into a syntax -- in this case, turn a symbol into an identifier. An identifier is basically a symbol with some lexical information that (very roughly) indicates how it is bound. Using datum->syntax you can do exactly that: it expects an existing piece of syntax which is where it copies the binding from, and a datum (a symbol here) which is the value that is contained in the syntax wrapper.

Here's an example that demonstrates a define-struct-like tool using this:

#lang scheme
;; implements a defstruct-like macro that uses association lists
(define-syntax (defstruct-lite stx)
  (syntax-case stx ()
    [(defstruct-lite name field ...)
     (let ([make-id
            (lambda (template . ids)
              (let ([str (apply format template (map syntax->datum ids))])
                (datum->syntax stx (string->symbol str))))])
       (with-syntax ([make-name (make-id "make-~a" #'name)]
                     [name?     (make-id "~a?" #'name)]
                     [(arg ...) (generate-temporaries #'(field ...))]
                     [(name-field ...)
                      (map (lambda (f) (make-id "~a-~a" #'name f))
                           (syntax->list #'(field ...)))])
         #'(begin
             (define (make-name arg ...) (list 'name (cons 'field arg) ...))
             (define (name? x) (and (pair? x) (eq? 'name (car x))))
             (define (name-field x)
               (and (name? x) (cdr (assq 'field (cdr x)))))
             ...)))]))

And here's an example of using it:

(defstruct-lite point x y)
(point-y (make-point 1 2))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文