【问题标题】:Why does foo have the same value as (foo) in this Racket macro?为什么 foo 在这个 Racket 宏中与 (foo) 具有相同的值?
【发布时间】:2021-03-20 21:00:02
【问题描述】:

我正在尝试了解 Racket 环境中的宏。这个概念让我很感兴趣。

在 Racket 博士的定义窗口上写下这个定义后:

(define-syntax foo
    (lambda (stx)
      (syntax "I am foo")))

我使用 REPL 调用了以下表达式:

> foo
"I am foo"

> (foo)
"I am foo"

这些结果让我感到惊讶。我期待像#procedure 这样的第一次调用foo

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

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

提前致谢。

【问题讨论】:

    标签: macros racket read-eval-print-loop


    【解决方案1】:

    通常,我在 Racket 中添加括号时会非常小心。

    是的,你小心点是对的。它通常会有所作为。

    但是,在您的情况下,它似乎没有什么不同,因为您正在创建一个过于简单的宏,无论宏是作为常规转换器还是作为identifier macro 调用,它的扩展方式都相同.

    我期待第一次调用 foo 时会出现 #procedure 之类的东西。

    我想先解决这个问题。宏在语法上转换您的程序。例如,我可以编写一个翻转操作数的宏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),因为宏所做的是转换语法。它不会创建过程。

    最后的谜团是裸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
    

    【讨论】:

      猜你喜欢
      • 2011-12-27
      • 2010-12-12
      • 2017-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多