2024 Clojure状态调查!中分享你的想法。

欢迎!请参阅关于页面获取更多关于此工作方式的信息。

+1
Clojure

在查看新的功能接口支持时,我注意到当编译器应该知道表达式已经支持功能接口时(例如,当表达式是一个适当的reify实例时),甚至还会发出使用Java lambda的检测。

为了避免不必要的检查,我将Compiler.java中的maybeEmitFIAdapter更改为添加以下编译时测试。更改后,Clojure仍然可以干净地构建并通过所有测试。我并非编译器或ASM专家,因此这可能不是最好的方法,但我想类似的方法应该被认为是可行的。

		// if(exp instanceof IFn) { emitInvokeDynamic(targetMethod, fnInvokerMethod) }
		expr.emit(C.EXPRESSION, objx, gen);
		// FOLLOWING TEST ADDED:
		if (!(expr.hasJavaClass() && targetClass.isAssignableFrom(expr.getJavaClass()))) {
			gen.dup();
			gen.instanceOf(ifnType);

			// if not instanceof IFn, go to end
			Label endLabel = gen.newLabel();
			gen.ifZCmp(Opcodes.IFEQ, endLabel);

			// else adapt fn invoker method as impl for target method
			emitInvokeDynamicAdapter(gen, targetClass, targetMethod, FnInvokers.class, fnInvokerMethod);

			// end - checkcast that we have the target FI type
			gen.mark(endLabel);
		}
		gen.checkCast(samType);

2 个答案

+2

被glchapman选择
 
最佳答案

是的,我相信这已经是我们讨论了很久的事情,并且以某种形式实现了。

关键问题是编译器实际上不知道表达式的类型,它只知道正在报告的类型,而且这可能来自提示类型流,这可能与后来的运行时现实不符。

一个具体的例子

(defn remove [^Collection coll ^Predicate pred]
  (.removeIf coll pred)))

在这里,removeIf的调用需要一个Predicate,并且pred局部变量将会有一个Predicate类型。但Clojure将提示视为提示,而不是静态类型签名,你可以传递实际的Predicate或IFn。在发出代码中的类型检查允许两者透明地工作。

更微妙的是,你可能甚至只对pred参数提供了类型提示: (.removeIf coll ^Predicate pred) 以解决重载(在本例中不是这样,但有一些是这样的),即使你知道pred不是一个Predicate而FI适配正是你想要的。一种可能的解决方案是使用新的参数标签元数据来指示首选的重载 (^[_ Predicate] Collection/.removeIf coll pred) 但这需要非常懂行才容易理解和应用。

我们也研究了调用和let绑定情况的不同行为,也许可以对let这样做,但不适用于调用。最终,唯一的安全做法是在运行时进行检查。

接下来的问题是这个检查的成本,通常在我的性能测试中,我找不到可检测的性能成本 - 你通常是在将函数传递给其他某处,那才是真正的成本驱动因素(将Predicate传递给.filter或Function传递给.map等等)。特别是在较新的JVM和CPU上,如果你恰好在循环中,所有的操作都会得到优化和正确预测。

–1 投票
实际上完全是另一回事。
...