2

我发现许多“特殊形式”只是在后台使用星号版本的宏(fn*、let* 和所有其他形式)。

例如,在 fn 的情况下,它将解构能力添加到 fn* 单独不提供的组合中。我试图找到一些关于 fn* 自己可以做什么和不能做什么的详细文档,但我没有那么幸运。

它绝对支持:

  • &/总括指标

    (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语句以直接代码路径,将前几个参数绑定到所需的符号等)。

这是出于性能原因吗?也许有人甚至可以方便地链接到星号特殊形式的实际标准定义。

4

3 回答 3

5

Arity 选择利用 JVM 虚拟方法调度:每个 arity(从 0 到 20 个参数)都有自己的方法,并且有一个用于 21+-arg arities 的方法。

您可能会注意到该applyTo方法是类似于您建议的通用方法fn&。它的实现只是选择正确的专用方法的巨大开关。

于 2015-09-15T22:36:58.217 回答
3

是的,您可以在您建议的超基元之上将所有这些作为宏来完成fn&,而且它肯定会简化编译器的实现。没有这样做的原因部分是出于性能原因(它会相当慢,并且 JVM 已经具有基于 arity 的快速调度工具),部分是“装饰性的”:这意味着函数的每个 arity 都是不同的函数被编译到的 JVM 类的方法,这使得堆栈跟踪更好。这也有助于 JIT 更好地“理解”我们的功能,从而进行相应的优化。

于 2015-09-15T22:32:13.067 回答
0

我的猜测是,这是方便/可扩展性驱动的。编译器(其中 fn* 实际上是“定义”/处理的)是用 java 编写的,并处理最少需要的功能,以便引导语言,而 fn 是建立在它之上的宏。与其他一些形式相同。Rich 曾在某处声明他可以将编译器从 java 重写为 clojure,但没有看到好处(如果错了,请纠正我)。

于 2015-09-15T22:29:05.730 回答