我写了一个宏:
(defmacro te
[a b & c]
`(print
~(a b c)))
并运行
(te print 2 inc 4)
收到错误ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn user/eval8010 (form-init8519408738377285198.clj:1)
我跑
(macroexpand-1 '(te print 2 3 4)
;=> (clojure.core/print (3 4))
这意味着(print 2 (3 4))
返回(3 4)
?函数print
有错误吗?
我的clojure版本1.7.0,JVM版本1.8.0_65-b17
更新
好的。举例不容易理解这个例子。
即使我运行
(te print 2 inc 4)
user=> (te print 2 inc 4)
;=> 5nil
user=> (macroexpand-1 '(te print 2 inc 4))
;=> (clojure.core/print (inc 4))
它将打印5
并返回nil
,这意味着(print 2 (inc 4))
返回表单(inc 4)
?
答案 0 :(得分:8)
这与print
根本无关。
这意味着
(print 2 (3 4))
返回(3 4)
?
这不是它的意思,而且这是你问题的根源。这意味着"用第一个参数print
调用函数2
,用(3 4)
"的值调用第二个参数。表达式(3 4)
没有有效值,因为它表示"使用参数3
调用函数4
"这就是为什么你得到一个例外:3是一个Long(数字)并且不能被称为函数(在clojure内部,它没有实现IFn函数接口)。
作为旁注,如果我理解你想要实现的目标(我可能是错的),你的宏可以很容易地写成一个函数,这通常意味着你应该把它写成一个函数,因为函数更容易处理,并与其他功能更好地合作。
答案 1 :(得分:0)
请记住宏观时尚未评估宏天堂的参数 被调用。
当您像这样调用宏时:
(te print 2 inc 4)
参数绑定到以下内容:
'print
'2
'(inc 4)
(因为宏是可变参数,所有参数都超过前两个参数
进入c
)你最终得到的表达是:
`(打印〜('打印' 2'(包括4)))
问题在于:内部print
不与功能相同
clojure.core/print
。这是一个宏观,因此它是一个未评估的符号。你正在使用
在这种情况下,符号本身就是一个函数,而不是使用引用的函数
通过符号。
Clojure中的每个符号都是一个在地图中查找自己的函数。
查找需要两个
参数,键和默认值。在这种情况下,符号print
正试图查看
由于2不是地图,因此它返回默认值(inc 4)
。
回想一下,unquote(~
)意味着评估表达式并替换结果
进入宏生成的代码。
所以编译器评估:('print '2 '(inc 4))
然后返回(inc 4)
将其替换为原始表达。
这就是你最终得到(clojure.core/print (inc 4))
试试这个:
(macroexpand-1'(te blarg 2 inc 4))
你甚至得到完全相同的结果
虽然blarg
不是Clojure中任何地方定义的变量。
在你的另一个例子中,另一个答案是对的。如果你的宏的第三个参数不是函数,那么你会在那里得到另一个错误。
老实说,我不确定你要完成什么,所以我不确定这是否真的能回答你的问题。但这就是为什么你会看到你所看到的东西。