【问题标题】:Why does the macroexpand does not print the results of defmacro?为什么macroexpand 不打印defmacro 的结果?
【发布时间】:2020-07-23 23:22:49
【问题描述】:

我是 Clojure 的新手,目前正在尝试使用 defmacromacroexpand 调用一个简单的宏。

(ns tutorial.core
  (:gen-class)) ; namespace

(defn -main [& args]
    
      (defmacro Simple [] (println "Hello"))
      (macroexpand '(Simple))
    
    )

有什么我错过的吗?看起来程序运行没有任何问题,但结果没有按预期出现。

我希望结果打印为Hello,但此脚本没有输出结果。

【问题讨论】:

    标签: clojure


    【解决方案1】:

    前言:

    有关the best way to write a macro(恕我直言)的概述,请参阅这个过去的问题。


    答案:

    您不应该在main 函数中定义宏。试试这个:

    (ns demo.core)
    
    (defmacro happy
      []
      `(println "I'm happy!"))   ; *** notice the backtick! ***
    
    (defn -main [& args]
      (println :expanded (macroexpand '(happy)))
      (happy)
      )
    

    启动一个repl:

    ~/expr/demo > lein repl
    
    demo.core=> (macroexpand '(happy))
    ;=> (clojure.core/println "I'm happy!")
    

    我们看到它有效。尝试从命令行运行:

    ~/expr/demo > lein run
    :expanded (happy)   ;  <= ***** OOOPS!  *****
    I'm happy!
    

    尝试将单引号更改为语法引号(也称为反引号),然后运行:

    (defn -main [& args]
      (println :expanded (macroexpand `(happy)))
      (happy))
    
    ~/expr/demo > lein run
    :expanded (clojure.core/println I'm happy!)
    I'm happy!
    

    解释是语法引用将完全限定 Var happy => demo.core/happy(由于该语法,您可以在 happy 宏本身内部的 println Var 上看到相同的效果-引用)。这允许宏扩展正常工作。与单引号比较:

    (defn -main [& args]
      (println :expanded (macroexpand '(demo.core/happy)))
      (happy))
    
    ~/expr/demo > lein run
    :expanded (clojure.core/println I'm happy!)
    I'm happy!
    

    造成这种行为的原因是,在 REPL 中,我们从提示中看到我们在 demo.core 命名空间中,因此 happy 被解析为 demo.core/happy。但是,当我们使用lein run 时,请注意:

    (defn -main [& args]
      (println *ns*)
      (println (ns-name *ns*)))
    

    结果:

    ~/expr/demo > lein run
    *ns*             => #object[clojure.lang.Namespace 0xb625b00 "user"]
    (ns-name *ns*)   => user
    

    我们看到*ns* 设置为user 命名空间,并且happy 无法解析为Var demo.core/happy,除非我们手动或在代码中使用syntax-quote 完全限定它。


    您可以找到list of documentation here。请务必特别学习 Clojure CheatSheet。

    对于宏,书Mastering Clojure Macros也不错。

    【讨论】:

      【解决方案2】:

      您的宏函数不返回代码,而是立即进行打印。这是非常糟糕的风格,因为它会产生无法预料的后果。

      如果你要使用这是一个函数:

      (defn hello [] (Simple))
      

      创建函数时它会打印“Hello”。插入到函数中的代码是println 的结果,即nil,因此您创建了这个:

      (defn hello [] nil)
      

      然后,如果您调用hello 3 次,则所有调用都不会打印任何内容,因为您的宏不会产生除nil 之外的任何内容。如果您将宏更改为返回结构:

      ;; The quote that makes all the difference
      (defmacro Simple [] '(println "Hello"))
      

      那么如果在hello的创建过程中不会打印任何东西,但是扩展会是(println "Hello"),同样的功能hello会变成:

      (defn hello [] (println "Hello"))
      

      【讨论】:

        猜你喜欢
        • 2020-06-22
        • 1970-01-01
        • 2016-05-21
        • 1970-01-01
        • 2016-08-31
        • 1970-01-01
        • 2019-04-19
        • 2014-12-06
        • 1970-01-01
        相关资源
        最近更新 更多