高种类型的隐式参数解析
高种类型的隐式参数解析
考虑以下代码:
object foo { trait Bar[Q[_]] implicit object OptionBar extends Bar[Option] def test[T, C[_]](c: C[T])(implicit bar: Bar[C]) = () def main(args: Array[String]) { test(Some(42): Option[Int]) //??? } }
这段代码是可以工作的,但是我需要将Some(42)的类型标注为Option[Int],否则隐式对象OptionBar将无法解析(因为期望的是Bar[Some])。有没有办法避免显式标注类型,使得我无论传入Some还是None,都能在test中得到隐式的OptionBar对象?
[澄清]
- 我这里使用Option只是举个例子,如果对于一个抽象类等情况也应该适用。
- 解决方案应该在其他没有关联的Bar也在作用域内时也能工作,比如
implicit object listBar extends Bar[list]
[更新]
似乎将Bar的参数逆变可以解决问题:
object foo { trait Bar[-Q[_]] //<--------------- implicit object OptionBar extends Bar[Option] implicit object ListBar extends Bar[List] def test[T, C[_]](c: C[T])(implicit bar: Bar[C]) = () def main(args:Array[String]) { test(Some(42)) } }
但是这当然是对Bar可能性的严重限制,所以我仍然希望能得到一个更好的答案。
隐式参数解析对于高阶类型的问题产生的原因是因为`Some(42)`比`Option[Int]`更具体,它是一个`Some[Int]`类型。下面是另一种编码方式:
object foo { trait Bar[Q[_]] implicit object OptionBar extends Bar[Option] def test[T, C[_]](c: C[T])(implicit bar: Bar[C]) = () def main(args: Array[String]) { test(Option(42)) } }
我并不支持使用`Option.apply`,除非是用它来编码空值检查。顺便说一下,使用scalaz,我会写成`42.some`或`42.pure[Option]`,它们都是`Option[Int]`类型。
为什么不推荐使用`Option.apply`?
当我有一个可能为空的引用想要转换为`Some`或`None`时,我会使用它。我可以阅读`Some(x) : Option[A]`并推断我有一个不需要追踪`x`来源的`Some`。如果参数是一个字面量,就像这个例子,那是可以的。
在这里我使用了`Option`只是作为一个例子。当我有抽象基类的“Bars”时,我想避免显式类型标注。
在上述代码中,有一个问题是在调用test方法时,编译器无法正确地推断出类型参数T和C。尽管已经定义了implicit object OptionBar作为Bar[Option]的实例,但是编译器仍然无法自动找到它。这是因为编译器对于高阶类型参数的隐式解析存在一些限制。
为了解决这个问题,可以使用更明确的类型约束来帮助编译器正确地推断类型参数。在第一个示例中,通过添加一个额外的类型约束D <: C[T],来限定参数D必须是类型C[T]的子类型。这样一来,编译器就可以正确地推断出类型参数T和C,从而找到正确的隐式实例。
然而,这种解决方法并不总是适用。如果在相同的作用域中添加另一个Bar的隐式实例,比如implicit object ListBar extends Bar[List],就会出现"ambiguous implicit values"错误。这是因为编译器无法确定应该选择哪一个隐式实例。
隐式参数解析对于高阶类型存在一些限制。为了解决这个问题,可以使用更明确的类型约束来帮助编译器正确地推断类型参数。然而,这种解决方法并不总是适用,可能会导致"ambiguous implicit values"错误。至今为止,还没有一个通用的解决方案来处理这个问题。