带有类型标记的集合在扩展方法中无法识别

时间:2013-12-06 14:08:55

标签: scala collections types

我想使用tagged types来表明集合是否已排序。例如:

type Tagged[U] = { type Tag = U }
type @@[T, U] = T with Tagged[U]

class Tagger[U] {
  def apply[T](t : T) : T @@ U = t.asInstanceOf[T @@ U]
}
def tag[U] = new Tagger[U]

trait Sorted

现在我想定义一个需要对集合进行排序的方法:

implicit final class RichIndexedSeq[A, CC <: IndexedSeq[A]](val sq: CC) 
  extends AnyVal {

  /** Nearest percentile (rounded index, no interpolation). */
  def percentile(n: Int)(implicit sorted: CC <:< Tagged[Sorted]): A = 
    sq((sq.size * n - 50) / 100)
}

我更希望将标记检查作为隐式证据参数,因为将会有其他方法进入RichIndexedSeq而没有该约束。

但是,我必须做错事。我希望这可行:

val x = Vector.fill(10)((math.random * 100).toInt)
val y = tag[Sorted](x.sorted)

但事实并非如此:

y.percentile(50)

<console>:44: error: value percentile is not a member of 
                     @@[scala.collection.immutable.Vector[Int],Sorted]
              y.percentile(50)
                ^

问题必须来自扩展方法间接,因为这有效:

implicitly[Vector[Int] @@ Sorted <:< Tagged[Sorted]]

4 个答案:

答案 0 :(得分:2)

implicit final class RichIndexedSeq[A, CC[_] <: IndexedSeq[_]](val sq: CC[A] @@ Sorted)
  extends AnyVal {

  def percentile(n: Int) = 
    sq((sq.size * n - 50) / 100)
}

答案 1 :(得分:2)

我认为你想让Sorted标签成为隐式类的类型签名的一部分,你现在拥有它的方式,你正在寻找一些用Sorted标记的隐式值,但是你没有隐式值在任何地方,您只需从IndexedSeqRichIndexedSeq进行隐式转换。这就是我的建议:

implicit final class RichSortedIndexedSeq[AA, CC[_] <: IndexedSeq[_]](val sq: CC[AA] @@ Sorted) 
  extends AnyVal {

  /** Nearest percentile (rounded index, no interpolation). */
  def percentile(n: Int): AA =
    sq((sq.size * n - 50) / 100)
}

而且我应该解决这个问题,但是,这似乎暴露了一个编译器错误。当我保留显式声明的返回类型百分位数时,我得到了这个编译器错误:

/tmp/sorted.scala:18: error: type mismatch;
 found   : A
 required: AA
    sq((sq.size * n - 50) / 100)
      ^
one error found

我们可以告诉这是一个错误,因为它正在寻找一个名为A的类型,但是我们没有在任何地方讨论这样的类型,这是一个编译器错误,其中IndexedSeq的类型变量以某种方式泄漏。这似乎是this bug的一种表现形式,此后已被修复,但我不确定scalac的已发布版本(如果有)包含修复程序。

答案 2 :(得分:1)

你看错了地方。这里:

implicit final class RichIndexedSeq[A, 
  CC <: IndexedSeq[A]] // This A is *NOT INFERRED* from the previous one
  (val sq: CC)

那里没有任何内容可以导致A的推断,而这正是造成问题的原因。见这里:

scala> new RichIndexedSeq(y)
<console>:19: error: inferred type arguments [Nothing,@@[scala.collection.immutable.Vector[Int],Sorted]] do not conform to class R
ichIndexedSeq's type parameter bounds [A,CC <: IndexedSeq[A]]
              new RichIndexedSeq(y)
              ^
<console>:19: error: type mismatch;
 found   : @@[scala.collection.immutable.Vector[Int],Sorted]
    (which expands to)  scala.collection.immutable.Vector[Int] with AnyRef{type Tag = Sorted}
 required: CC
              new RichIndexedSeq(y)
                                 ^

事实上,让我们试试x

scala> new RichIndexedSeq(x)
<console>:16: error: inferred type arguments [Nothing,scala.collection.immutable.Vector[Int]] do not conform to class RichIndexedS
eq's type parameter bounds [A,CC <: IndexedSeq[A]]
              new RichIndexedSeq(x)
              ^
<console>:16: error: type mismatch;
 found   : scala.collection.immutable.Vector[Int]
 required: CC
              new RichIndexedSeq(x)
                                 ^

现在,让我们明确一下类型参数:

scala> new RichIndexedSeq[Int, @@[Vector[Int], Sorted]](y)
res6: RichIndexedSeq[Int,@@[Vector[Int],Sorted]] = RichIndexedSeq@1a925d98

测试它:

scala> res6.percentile(50)
res8: Int = 49

为了解决这种类型的推理问题,有一个叫做IsTraversableLike的特征。不幸的是,由于需要保留标签,它的使用似乎很奇怪,但我建议你阅读它 - 你可能会找到一种方法来做,或者你可能会写一个类似的特性来满足你的需求。

答案 3 :(得分:0)

由于ScalaC的错误(?)与精神分裂症类型(炖菜中的方法percentile并且Keniji的答案无法声明返回A),我最终放弃了输入集合类型手。这没关系,因为我没有在这个特定的方法中构建一个新的集合:

implicit class RichSortedIndexedSeq[A](val sq: IndexedSeq[A] @@ Sorted) 
  extends AnyVal {

  def percentile(n: Int): A = sq((sq.size * n - 50) / 100)
}