我想使用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]]
答案 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标记的隐式值,但是你没有隐式值在任何地方,您只需从IndexedSeq
到RichIndexedSeq
进行隐式转换。这就是我的建议:
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)
}