【问题标题】:collection with type tagging not recognised in extension method扩展方法中无法识别类型标记的集合
【发布时间】:2013-12-23 22:08:28
【问题描述】:

我想使用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]]

【问题讨论】:

    标签: scala collections types


    【解决方案1】:
    implicit final class RichIndexedSeq[A, CC[_] <: IndexedSeq[_]](val sq: CC[A] @@ Sorted)
      extends AnyVal {
    
      def percentile(n: Int) = 
        sq((sq.size * n - 50) / 100)
    }
    

    【讨论】:

    • 好的,但是知道为什么我不能将@@ Sorted 移动到后面的证据参数中吗?
    【解决方案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 的哪些已发布版本(如果有)包含修复。

    【讨论】:

    • 好的,所以这和 Kenji 的想法是一样的。他离开了percentile 的返回类型,这似乎可以编译。您(和我)遇到的类型不匹配非常奇怪!?
    • 是的,但是如果您关闭返回类型然后尝试对结果执行任何操作,它将无法编译。就像拿你的整数序列一样,调用百分位数,然后尝试在结果中加 1。它将尝试使用神秘的 A 类型执行 any2stringadd,而不是使用 Int 进行整数添加。
    【解决方案3】:

    你看错地方了。这里:

    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 的特征。不幸的是,它的用法似乎与保留标签的需要不一致,但我建议您阅读它——您可能会找到一种方法来做到这一点,或者您可能会编写一个满足您需求的类似特征。

    【讨论】:

      【解决方案4】:

      由于 ScalaC 错误 (?) 类型为精神分裂症(stew's 中的方法 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)
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-02-05
        • 2022-09-30
        • 2013-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多