Macros 为什么在这个Racket宏中foo的值与(foo)的值相同?

Macros 为什么在这个Racket宏中foo的值与(foo)的值相同?,macros,racket,read-eval-print-loop,Macros,Racket,Read Eval Print Loop,我试图理解球拍环境中的宏。我对这个概念很感兴趣 在Racket博士的定义窗口中写入此定义后: (define-syntax foo (lambda (stx) (syntax "I am foo"))) 我使用REPL调用以下表达式: > foo "I am foo" > (foo) "I am foo" 这些结果令我惊讶。我期待着对foo的第一次调用会有类似于#过程的东西 为什么(foo)和foo

我试图理解球拍环境中的宏。我对这个概念很感兴趣

在Racket博士的定义窗口中写入此定义后:

(define-syntax foo
    (lambda (stx)
      (syntax "I am foo")))
我使用REPL调用以下表达式:

> foo
"I am foo"

> (foo)
"I am foo"
这些结果令我惊讶。我期待着对foo的第一次调用会有类似于
#过程的东西

为什么(foo)和foo提供相同的输出

通常,我会非常小心地在球拍中添加括号。通常,它们会完全改变所调用表达式的含义。显然,在这种情况下,这没有什么区别

提前谢谢

通常,我会非常小心地在球拍中添加括号

是的,你小心是对的。这通常会带来不同

但是,在您的情况下,这似乎没有什么区别,因为您正在创建一个过于简单的宏,无论该宏是作为常规转换器调用还是作为转换程序调用,它都会以相同的方式展开

我期待着第一次打电话给foo时会有类似的程序

我想先解决这个问题。宏按语法转换程序。例如,我可以编写一个翻转操作数的宏
flip
,以便

(flip foo 1 (let) bar baz 2)
扩展(未评估)为:

我想再次强调,这是一种程序转换,就像您如何使用编辑器编辑代码一样

现在,让我们编写一些实际的宏:

(define-syntax bar
  (lambda (stx)
    (cond
      [(equal? (syntax->datum stx) '(bar abc def)) #'(+ 1 1)]
      [else #'(+ 2 2)])))

(bar abc def)      ;== expands => (+ 1 1) == evaluates => 2
(bar 42 (abc) qqq) ;== expands => (+ 2 2) == evaluates => 4
(bar)              ;== expands => (+ 2 2) == evaluates => 4
在上面的宏中,它检查输入语法在语法上是否为
(bar abc def)
。如果是,它将转换为
(+1)
。否则,它将转换为
(+2)

所有这些都是为了向您表明,期望宏产生“#过程”(当然,除非宏扩展为lambda)是不合理的,因为宏所做的是转换语法。它不会创建一个过程

最后一个谜团是bare
foo发生了什么事。让我们创建一个宏
baz
,以了解:

(define-syntax baz
  (lambda (stx)
    (cond
      [(equal? (syntax->datum stx) 'baz) #'1]
      [(equal? (syntax->datum stx) '(baz)) #'2]
      [else #'3])))

baz      ;== expands => 1
(baz)    ;== expands => 2
(baz 10) ;== expands => 3
事实证明,裸标识符也可以是宏

现在,考虑你的代码> Foo:

(define-syntax foo
    (lambda (stx)
      (syntax "I am foo")))
它是一种忽略其操作数的转换,并且总是扩展到
“I am foo”

因此:

请注意,在大多数宏中,我们使用模式匹配来提取操作数。当输入语法与任何模式都不匹配时,模式匹配会引发语法错误。例如,这允许我们创建一个不允许用作标识符宏的宏

(define-syntax food
  (lambda (stx)
    (syntax-case stx ()
      ;; match when there is a parenthesis around the macro
      [(_ ...) #'1])))

(food) ;=> 1
food   ;=> food: bad syntax
通常,我会非常小心地在球拍中添加括号

是的,你小心是对的。这通常会带来不同

但是,在您的情况下,这似乎没有什么区别,因为您正在创建一个过于简单的宏,无论该宏是作为常规转换器调用还是作为转换程序调用,它都会以相同的方式展开

我期待着第一次打电话给foo时会有类似的程序

我想先解决这个问题。宏按语法转换程序。例如,我可以编写一个翻转操作数的宏
flip
,以便

(flip foo 1 (let) bar baz 2)
扩展(未评估)为:

我想再次强调,这是一种程序转换,就像您如何使用编辑器编辑代码一样

现在,让我们编写一些实际的宏:

(define-syntax bar
  (lambda (stx)
    (cond
      [(equal? (syntax->datum stx) '(bar abc def)) #'(+ 1 1)]
      [else #'(+ 2 2)])))

(bar abc def)      ;== expands => (+ 1 1) == evaluates => 2
(bar 42 (abc) qqq) ;== expands => (+ 2 2) == evaluates => 4
(bar)              ;== expands => (+ 2 2) == evaluates => 4
在上面的宏中,它检查输入语法在语法上是否为
(bar abc def)
。如果是,它将转换为
(+1)
。否则,它将转换为
(+2)

所有这些都是为了向您表明,期望宏产生“#过程”(当然,除非宏扩展为lambda)是不合理的,因为宏所做的是转换语法。它不会创建一个过程

最后一个谜团是bare
foo发生了什么事。让我们创建一个宏
baz
,以了解:

(define-syntax baz
  (lambda (stx)
    (cond
      [(equal? (syntax->datum stx) 'baz) #'1]
      [(equal? (syntax->datum stx) '(baz)) #'2]
      [else #'3])))

baz      ;== expands => 1
(baz)    ;== expands => 2
(baz 10) ;== expands => 3
事实证明,裸标识符也可以是宏

现在,考虑你的代码> Foo:

(define-syntax foo
    (lambda (stx)
      (syntax "I am foo")))
它是一种忽略其操作数的转换,并且总是扩展到
“I am foo”

因此:

请注意,在大多数宏中,我们使用模式匹配来提取操作数。当输入语法与任何模式都不匹配时,模式匹配会引发语法错误。例如,这允许我们创建一个不允许用作标识符宏的宏

(define-syntax food
  (lambda (stx)
    (syntax-case stx ()
      ;; match when there is a parenthesis around the macro
      [(_ ...) #'1])))

(food) ;=> 1
food   ;=> food: bad syntax