在宏中调用其他函数 [英] calling other functions in macros

查看:45
本文介绍了在宏中调用其他函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何在宏中调用其他函数/宏?以下似乎不起作用(即使我用 define-syntax 定义了 bar)

How can I call other functions/macros within a macro? The following doesn't seem to work (even if I define bar with define-syntax)

(define (bar) #'"hello")

(define-syntax (foo stx)
  (syntax-case stx ()
    [(_ '(a b)) (bar)]))

推荐答案

Racket 的宏系统在运行时和编译时代码之间保持谨慎的分离.您将 bar 定义为运行时函数,但您实际上想在宏中使用它.因此,您需要在编译时通过用 begin-for-syntax 包装它来显式定义 bar:

Racket’s macro system maintains careful separation between runtime and compile-time code. You defined bar as a runtime function, but you actually want to use it in a macro. Therefore, you need to explicitly define bar at compile-time by wrapping it with begin-for-syntax:

(begin-for-syntax
  (define (bar) #'"hello"))

这将解决您的问题.有关详细信息,请参阅 The Racket 中的编译和运行时阶段指南.

This will solve your problem. For more information, see Compile and Run-Time Phases in The Racket Guide.

为什么需要这样做?嗯,各种原因.首先,通过明确区分运行时代码和编译时代码,编译器可以对代码何时加载做出一定的保证.例如,您可能会在宏的实现中使用一个库,但您可能永远不会在运行时使用该库.通过注意分离运行时和编译时,编译器可以确保只在编译时加载库,而不是在运行时加载.

Why is this necessary? Well, various reasons. First of all, by explicitly distinguishing runtime code from compile-time code, the compiler can make certain guarantees about when code is loaded. For example, you might use a library in the implementation of a macro, but you might never use the library at runtime. By taking care to separate runtime and compile-time, the compiler can be sure to only bother loading the library at compile-time, not at runtime.

在 Racket 中,我们将代码可以运行的不同时间称为 阶段,并为每个阶段分配一个数字.例如,运行时是 phase 0,编译时是 phase 1.为什么要费心使用数字?好吧,事实证明不仅仅是两个阶段!事实上,Racket 中可以有任意数量的编译阶段,继续阶段 2、阶段 3,依此类推.

In Racket, we call the different times that code can run phases, and we assign each phase a number. For example, runtime is phase 0 and compile-time is phase 1. Why bother using numbers? Well, it turns out there are more than just two phases! In fact, there can be an arbitrary number of compilation phases in Racket, continuing with phase 2, phase 3, and so on.

如果第一阶段是编译期,那么第二阶段是什么?那么,如果你在另一个宏的实现中使用一个宏怎么办?直接试的话是不行的:

So if phase 1 is compile-time, what is phase 2? Well, what if you use a macro in the implementation of another macro? If you try directly, it won’t work:

(define-syntax (foo stx)
  (syntax-case stx ()
    [(_) #''foo]))

(define-syntax (bar stx)
  (syntax-case stx ()
    [(_) (foo)]))

再一次,上面的程序会抱怨 foo 未绑定,因为 foo 是在第 0 阶段定义的,但是 bar 中的代码处于阶段 1.因此,我们需要将 foo 的定义包裹在 begin-for-syntax 中,就像之前一样:

Once again, the above program will complain that foo is unbound, since foo is defined at phase 0, but the code inside bar is at phase 1. Therefore, we need to wrap the definition of foo in begin-for-syntax, just like before:

(begin-for-syntax
  (define-syntax (foo stx)
    (syntax-case stx ()
      [(_) #''foo])))

但问题是:实现 foo 的代码处于哪个阶段?它显然不是第 0 阶段,因为它是一个宏,但也不是第 1 阶段,因为它是一个在编译时定义的宏(因为它被包裹在 begin-for-语法).因此,foo 的主体处于阶段 2!

But here’s the question: what phase is the code that implements foo at? It obviously isn’t phase 0, since it’s a macro, but it isn’t phase 1, either, since it’s a macro defined at compile-time (since it’s wrapped in begin-for-syntax). Therefore, the body of foo is at phase 2!

确实,如果你尝试编写上面的代码,你可能会得到一些错误,很多东西都没有绑定.当您编写#lang racket 时,事物会在阶段 0 和阶段 1 自动导入,但一般而言,模块也仅在个别阶段导入.为了使上面的代码片段起作用,我们需要在第 2 阶段导入 racket/base,如下所示:

Indeed, if you try to write the above code, you may get some errors that lots of things are unbound. When you write #lang racket, things are automatically imported at phase 0 and phase 1, but in general, modules are only imported at individual phases as well. To make the above snippet work, we’d need to import racket/base at phase 2, like this:

(require (for-meta 2 racket/base))

阶段的所有细节都超出了这个答案的范围,但我要强调的是 Racket 中的阶段很重要,当您编写宏时,您必须担心它们.如需更彻底的处理,请参阅 The Racket Guide 中的 General Phase Levels,其中补充并扩展了之前链接的介绍部分.有关为什么相级别很重要以及没有相分离时会出现什么问题的更多详细信息,请参阅论文的(非常易读的)第一部分 Composable and Compilable Macros,首先介绍了 Racket 的模块系统.

All the details of phases are outside the scope of this answer, but the point I’m making is that phases in Racket are important, and when you’re writing macros, you have to worry about them. For a more thorough treatment, see General Phase Levels in The Racket Guide, which complements and extends the previously-linked introductory section. For even more details about why phase levels are important and what goes wrong when you don’t have phase separation, see the (very readable) first section of the paper Composable and Compilable Macros, which first introduced Racket’s module system.

这篇关于在宏中调用其他函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆