集合扩展方法,保留了元素类型A和表示类型Repr



作为上一个问题的后续,我是否可以设计一个隐式类来处理两种类型的SeqLike扩展:

import collection.SeqLike
import collection.generic.CanBuildFrom
implicit class Test[A, Repr](val sq: SeqLike[A, Repr]) extends AnyVal {
  // no constraints on Repr
  def foo[B](f: A => B)(implicit ord: Ordering[B]): Repr = sq.sortBy(f)
  // constraint that actually sq is Repr
  def bar[B, To](fun: (A, A) => B)(implicit cbf: CanBuildFrom[Repr, B, To]): To = {
    val b = cbf(sq)  // NO!
    // ...
    b.result
  }
}

bar方法没有编译,因为我们缺少typeOf(sq) == Repr的约束。正如其他人所指出的,如果我将构造函数更改为sq: Repr,我们将失去与类型A的连接。

现在,Repr断开连接。例如:

// in Test:
def isSortedBy[B](fun: A => B)(implicit ord: Ordering[B]): Boolean =
  sq.sliding(2, 1).forall {
    case a +: b +: _  => ord.lteq(fun(a), fun(b))
    case _            => true  // happens when it size == 1
  }
[error] Test.scala: inferred type arguments [T,Equals] do not conform to method 
                    unapply's type parameter bounds 
                    [T,Coll <: scala.collection.SeqLike[T,Coll]]
[error]         case a +: b +: _  => ord.lteq(fun(a), fun(b))
[error]                ^

您可以使用repr,并将sq的类型更改为SeqLike,而不是Repr。但与我以前的解决方案不同,您可以将上类型绑定到ReprRepr<:SeqLike[A,Repr]),以解决剩余的键入问题。举例:

implicit class Test[A, Repr<:SeqLike[A,Repr]](val sq: SeqLike[A, Repr]) extends AnyVal {
  // no constraints on Repr
  def foo[B](f: A => B)(implicit ord: Ordering[B]): Repr = sq.sortBy(f)
  // constraint that actually sq is Repr
  def bar[B, To](fun: (A, A) => B)(implicit cbf: CanBuildFrom[Repr, B, To]): To = {
    val b = cbf(sq.repr)  // NO!
    var x = sq.head
    sq.tail.foreach { y =>
      b+=fun(x,y)  
      x = y
    }
    b.result
  }
  def isSortedBy[B](fun: A => B)(implicit ord: Ordering[B]): Boolean =
    sq.sliding(2, 1).forall {
      case a +: b +: _  => ord.lteq(fun(a), fun(b))
      case _            => true  // happens when it size == 1
    }
}

因此,如果我正确理解问题,

  • 如果将类参数sq设置为Repr,则会丢失类型A(不再有任何推断),

  • 但如果您将其设置为SeqLike[A, Repr],那么您将失去也是Repr的事实,因此cbf(sq)不再编译。

那么两者都做呢:

implicit class Test[A, Repr](val sq: Repr with SeqLike[A, Repr]) extends AnyVal {
   ...
}

这个版本编译得很好,我可以在一个简单的List(1,2,3)上调用foobar

编辑:如果要将sq以外的类型Repr的值作为集合进行操作,还需要Régis的答案中的类型绑定Repr <: SeqLike[A,Repr]

sq: Repr with SeqLike[A, Repr]唯一能给你的是,你可以在任何地方使用sq而不是sq.repr,因为它已经被正确地键入了。

最新更新