是的,请相信我,这确实是我们讨论了好久的事情,并且它已经以某种形式得到了实施。
关键是编译器实际上不知道表达式的类型,它只知道正在报告的类型,而且这可能来自提示类型流,而这些流可能与后来的运行时现实不符。
具体例子
(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 上,如果你碰巧在一个循环中,所有这些都将被优化并正确预测。