Scala上下文绑定意外不起作用

时间:2013-08-05 12:37:00

标签: scala seq context-bound

我试图定义一个函数,用于检查泛型Seq是否已排序。

我想出了这个:

import Ordering.Implicits._

def isOrdered[A: Ordering](seq: Seq[A]): Boolean =
  seq.sliding(2).map({ case List(a, b) => b > a }).forall(identity)

此时,编译器会抛出“没有为A定义的隐式排序”。

我可以通过将a和b归为以下来解决这个问题:

def isOrdered[A: Ordering](seq: Seq[A]): Boolean =
  seq.sliding(2).map({ case List(a: A, b: A) => a < b }).forall(identity)

此时编译器很乐意接受该函数。

我感到好奇的是,以下实现开箱即用:

def isOrdered[A: Ordering](seq: Seq[A]): Boolean =
  seq.sliding(2).exists{s => s(0) > s(1)}

据我所知,唯一显着的区别是我不使用部分功能。

任何人都可以解释这种行为吗?

2 个答案:

答案 0 :(得分:1)

接受的答案对我来说非常不满意,因为我仍然不知道发生了什么。

我的第一反应是,它必须是暗示+推断的错误,无论是否是他们所知道的。

问题不在于map的类型参数,因为它减少到匹配:

scala> def f[X: Ordering](seq: Seq[X]) = seq match { case List(a,b) => b > a }

怎么能不起作用?我的猜测是它将与Ordered的不变性有关,因此将List.unapply解包的方式与输入Seq进行比较意味着我们不能依赖于范围中的隐式Ordering。

让我们打开一些调试。 (-Xprint:typer,patmat,-Xlog-implicits,-Yinfer-debug)

这就是case Seq在typer上翻译的方式:

def f[A](seq: Seq[A])(implicit evidence$1: Ordering[A]): Boolean = seq match {
  case collection.this.Seq.unapplySeq[A](<unapply-selector>) <unapply> ((a @ _), (b @ _)) => scala.`package`.Ordering.Implicits.infixOrderingOps[A](b)(evidence$1).>(a)

并在patmat:

def f[A](seq: Seq[A])(implicit evidence$1: Ordering[A]): Boolean = {
  case <synthetic> val x1: Seq[A] = seq;
  case5(){
    <synthetic> val o7: Option[Seq[A]] = collection.this.Seq.unapplySeq[A](x1);
    if (o7.isEmpty.unary_!)
      if (o7.get.!=(null).&&(o7.get.lengthCompare(2).==(0)))
        {
          val a: A = o7.get.apply(0);
          val b: A = o7.get.apply(1);
          matchEnd4(scala.`package`.Ordering.Implicits.infixOrderingOps[A](b)(evidence$1).>(a))
        }
      else
        case6()
    else
      case6()
  };

换句话说,unapply只返回你的Seq,它得到前两个元素。

case List应该看起来完全一样,除非它没有输入检查。

好吧,我被其他事情分心了,比如我的女儿今天开始在水下游泳,所以要短路,这样做有效:

scala> import Ordering.Implicits.infixOrderingOps
import Ordering.Implicits.infixOrderingOps

scala> import reflect.ClassTag
import reflect.ClassTag

scala> def f[X](seq: Seq[X])(implicit e1: Ordering[X], e2: ClassTag[X]) = seq match { case xs: List[X] if xs.length == 2 => xs(1) > xs(0) }
f: [X](seq: Seq[X])(implicit e1: Ordering[X], implicit e2: scala.reflect.ClassTag[X])Boolean

也许this deficit is at play

答案 1 :(得分:0)

在第一种情况下,

{ case List(a, b) => b > a }

这不起作用,因为您刚刚定义了部分函数,​​但没有定义这些元素的排序。从某种意义上说,case List(a,b)代表一个包含ab的列表。现在a可能意味着什么。 执行a:A, b:A是有效的,因为现在您定义它是一个包含2个A类型元素的List。

因为我们已为A定义了订购。因此第二个工作。

根据第三个:seq.sliding(2).返回Iterator[List[A]]。对于迭代器中的每个List[A],您调用s(0) > s(1)。此处s(0)s(1)的类型为A。因为已定义了绑定A: Ordering,所以为类型A定义了排序。在这种情况下s(0) s(1)。因此它有效。