我发现很多"特殊形式"只是在后台使用星号版本的宏(fn *,让*和其他所有版本)。
例如,在fn的情况下,它将解构能力添加到fn *单独不提供的混合中。我试图找到一些关于fn *能够和不能自己做什么的详细文档,但我没那么幸运。
它肯定支持:
& / catchall指标
(fn* [x & rest] (do-smth-here...))
并且好奇地还有arity上的超载,如:
(fn* ([x] (smth-with-one-arg ...)
([x y] (smth-with-two-args ...))
所以最后我的问题是,为什么不定义:
(fn& [& all-args] ...)
这绝对是最小的,可以通过宏提供所有的arity选择(检查参数列表的大小,if / case语句直接代码路径,将前几个参数绑定到所需的符号等)。
这是出于性能原因吗?也许有人甚至可以链接到星号特殊形式的实际标准定义。
答案 0 :(得分:5)
Arity选择利用JVM虚拟方法调度:each arity (from 0 to 20 arguments) has its own method,并且有21 + -arg arities的单一方法。
您可能会注意到applyTo
方法,这种方法类似于您对fn&
的建议。它的实现只是giant switch来选择正确的专用方法。
答案 1 :(得分:3)
是的,你可以在你提出的超原始fn&
之上做所有这些宏,它肯定会简化编译器的实现。之所以没有这样做,部分是出于性能原因(它会相当缓慢,并且JVM已经有了基于arity的快速调度工具),而且部分"化妆品":这意味着每个arity都是函数是编译函数的JVM类的不同方法,这使得堆栈跟踪更好。这也有助于JIT"了解"我们的功能更好,因此可以相应地进行优化。
答案 2 :(得分:0)
我的猜测将是方便/可扩展性驱动。编译器(其中fn *实际上是“已定义”/处理过的)是用java编写的,它处理引导语言所需的最少功能,而fn是构建在它之上的宏。与其他一些形式相同。在某个地方有一个Rich的声明,他可以将编译器从java重写为clojure但是没有看到好处(如果错误则纠正我)。