为什么 foo 与这个 Racket 宏中的 (foo) 具有相同的值?
Why does foo have the same value as (foo) in this Racket macro?
我正在尝试了解 Racket 环境中的宏。这个概念让我很感兴趣。
在 Racket 博士的定义上写下这个定义后 window:
(define-syntax foo
(lambda (stx)
(syntax "I am foo")))
我使用 REPL 调用了以下表达式:
> foo
"I am foo"
> (foo)
"I am foo"
这些结果让我吃惊。对于 foo.
的第一次调用,我期待类似 #procedure
的结果
为什么 (foo) 和 foo 提供相同的输出?
通常,我在 Racket 中添加括号时非常小心。通常,它们会完全改变被调用表达式的含义。在这种情况下,显然,这没有什么区别。
提前致谢。
Usually, I am pretty careful about adding parenthesis in Racket.
是的,你小心是对的。它通常会有所作为。
然而,在你的情况下,它似乎没有什么区别,因为你正在创建一个过于简单的宏,无论宏是作为常规转换器还是作为 identifier macro.
I was expecting something like a #procedure for the first call on foo.
我想先解决这个问题。宏在句法上改变你的程序。例如,我可以编写一个翻转操作数的宏 flip
,这样
(flip foo 1 (let) bar baz 2)
扩展(未评估)为:
(2 baz bar (let) 1 foo)
再次强调,这是一个程序转换,就像你用编辑器编辑代码一样。
现在,让我们编写一些实际的宏:
(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 1)
。否则,它转换为 (+ 2 2)
.
所有这些都是为了向您表明,期望宏产生“#procedure”是不合理的(当然,除非宏扩展为 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"
。
所以:
(foo 1 2 3) ;== expands => "I am foo"
(foo x y z) ;== expands => "I am foo"
(foo) ;== expands => "I am foo"
foo ;== expands => "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
我正在尝试了解 Racket 环境中的宏。这个概念让我很感兴趣。
在 Racket 博士的定义上写下这个定义后 window:
(define-syntax foo
(lambda (stx)
(syntax "I am foo")))
我使用 REPL 调用了以下表达式:
> foo
"I am foo"
> (foo)
"I am foo"
这些结果让我吃惊。对于 foo.
的第一次调用,我期待类似#procedure
的结果
为什么 (foo) 和 foo 提供相同的输出?
通常,我在 Racket 中添加括号时非常小心。通常,它们会完全改变被调用表达式的含义。在这种情况下,显然,这没有什么区别。
提前致谢。
Usually, I am pretty careful about adding parenthesis in Racket.
是的,你小心是对的。它通常会有所作为。
然而,在你的情况下,它似乎没有什么区别,因为你正在创建一个过于简单的宏,无论宏是作为常规转换器还是作为 identifier macro.
I was expecting something like a #procedure for the first call on foo.
我想先解决这个问题。宏在句法上改变你的程序。例如,我可以编写一个翻转操作数的宏 flip
,这样
(flip foo 1 (let) bar baz 2)
扩展(未评估)为:
(2 baz bar (let) 1 foo)
再次强调,这是一个程序转换,就像你用编辑器编辑代码一样。
现在,让我们编写一些实际的宏:
(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 1)
。否则,它转换为 (+ 2 2)
.
所有这些都是为了向您表明,期望宏产生“#procedure”是不合理的(当然,除非宏扩展为 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"
。
所以:
(foo 1 2 3) ;== expands => "I am foo"
(foo x y z) ;== expands => "I am foo"
(foo) ;== expands => "I am foo"
foo ;== expands => "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