高种类型的隐式参数解析

12 浏览
0 Comments

高种类型的隐式参数解析

考虑以下代码:

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可能性的严重限制,所以我仍然希望能得到一个更好的答案。

0
0 Comments

隐式参数解析对于高阶类型的问题产生的原因是因为`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”时,我想避免显式类型标注。

0
0 Comments

在上述代码中,有一个问题是在调用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"错误。至今为止,还没有一个通用的解决方案来处理这个问题。

0