Clojure 计算宏时出现NullPointerException

Clojure 计算宏时出现NullPointerException,clojure,Clojure,我正在编写一个宏来实现当内置宏时,代码如下: (defmacro when-valid "Macro that does a series of actions if the condition is true" [condition & actions] `(if ~condition (~cons 'do ~actions) nil)) 但当我这样评价它时: (when-valid (< 1 2) (println "hello") (println "d

我正在编写一个宏来实现
内置宏时,代码如下:

(defmacro when-valid
  "Macro that does a series of actions if the condition
    is true"
  [condition & actions]
  `(if ~condition (~cons 'do ~actions) nil))
但当我这样评价它时:

(when-valid (< 1 2) (println "hello") (println "dear"))
我应该得到
nil
而不是
NullPointerException
。 有人知道我做错了什么吗


谢谢。

让我们看看宏观扩张阶段会发生什么:

(macroexpand '(when-valid (< 1 2) (println "hello") (println "dear")))
当Clojure计算此代码时,它将看到
(println“hello”)
是一个列表,因此它将对其进行计算,假设返回的结果是一个函数,并尝试调用该函数。当然,
(println“hello”)
返回
nil,
因此您得到一个
NullPointerException

为什么会这样?让我们仔细看看你的宏观经济正在做什么:

(defmacro when-valid
  "Macro that does a series of actions if the condition is true"
  [condition & actions]
  `(if ~condition
     (~cons 'do ~actions)
     nil))
这将返回一个列表,其前两项是
if
符号和
条件
表达式,最后一项是
nil
。到目前为止,一切顺利。但是在“then”子句中,不是得到一个由
do
符号和
actions
表达式组成的列表,而是得到一个包含以下三项的列表:

  • cons
    函数(不是符号,因为您使用
    ~
    解析
    cons
  • 表达式
    'do
    ,扩展为
    (引号do)
  • 操作
    表达式包装在一个列表中,因为您使用了unquote(
    ~
    )而不是unquote-splice(
    ~@
  • 你真正想要的是:

    (defmacro when-valid
      "Macro that does a series of actions if the condition is true"
      [condition & actions]
      `(if ~condition
         (do ~@actions)))
    

    自从“那时”表达式仍在语法引号内,
    do
    符号将被正确引用。通过使用
    ~@
    ,您可以将
    操作
    序列展开为一个列表,在开始时使用
    do
    ,而不是将其包装为一个序列,这将导致上述问题。我还省略了最后的de>nil,因为它隐含在没有“else”子句的
    if
    表达式中。

    让我们看看在宏扩展阶段会发生什么:

    (macroexpand '(when-valid (< 1 2) (println "hello") (println "dear")))
    
    当Clojure计算此代码时,它将看到
    (println“hello”)
    是一个列表,因此它将对其进行计算,假设返回的结果是一个函数,并尝试调用该函数。当然,
    (println“hello”)
    返回
    nil,
    因此您会得到一个
    NullPointerException

    为什么会这样呢?让我们仔细看看你的宏观经济正在做什么:

    (defmacro when-valid
      "Macro that does a series of actions if the condition is true"
      [condition & actions]
      `(if ~condition
         (~cons 'do ~actions)
         nil))
    
    这将返回一个列表,其前两项是
    if
    符号和
    条件
    表达式,最后一项是
    nil
    。到目前为止,一切正常。但在“then”中子句,而不是得到一个由
    do
    符号和
    actions
    表达式组成的列表,而是得到一个包含以下三项的列表:

  • cons
    函数(不是符号,因为您使用
    ~
    解析
    cons
  • 表达式
    'do
    ,扩展为
    (引号do)
  • 操作
    表达式包装在一个列表中,因为您使用了unquote(
    ~
    )而不是unquote-splice(
    ~@
  • 你真正想要的是:

    (defmacro when-valid
      "Macro that does a series of actions if the condition is true"
      [condition & actions]
      `(if ~condition
         (do ~@actions)))
    
    自从“那时”表达式仍在语法引号内,
    do
    符号将被正确引用。通过使用
    ~@
    ,您可以将
    操作
    序列展开为一个列表,在开始时使用
    do
    ,而不是将其包装为一个序列,这将导致上述问题。我还省略了最后的de>nil,因为它隐含在不带“else”子句的
    if
    表达式中