是的,信我,这确实是我们讨论已久的事情,这个功能已经以各种各样的形式实现了。
关键是编译器实际上并不了解表达式的类型,它只知道报告的类型,这可以来自提示类型流,而这可能实际上并不匹配后来的运行时现实。
一个具体的例子
(defn remove [^Collection coll ^Predicate pred]
(.removeIf coll pred)))
在这里,调用removeIf需要一个谓词(Predicate)和pred局部变量的表达式类型,它将被视为谓词。但是Clojure将提示视为提示,而不是静态类型签名,因此您可以将实际的谓词传递给它,或者传递IFn。在生成的代码中进行的类型检查允许它们透明地工作。
更微妙的是,您甚至可能只为pred参数提供了类型提示:(.removeIf coll ^Predicate pred)
来解决重载(在这个案例中不是,但有一些是这样的),尽管您知道pred不是一个Predicate,而且FI适应就是您想要的。解决重载情况的一个可能方法是通过使用新的param-tags元数据来指示首选重载 (^[_ Predicate] Collection/.removeIf coll pred)
,但这需要真正聪明才能理解和应用。
我们还检查了调用和声明绑定情况的不同行为,以考虑将此行为应用于let,但不应用于调用。最后,唯一安全的事情是在运行时进行检查。
下一个问题是检查的成本,通常在我的性能测试中,我找不到可检测的性能成本 - 您通常将功能传递给其他某个东西,这是真正的成本驱动因素(将谓词传递给.filter,或将函数传递给.map等)。特别是在较新的JVM和CPU上,如果您在循环中,所有这些都会得到优化和正确预测。