在Scala 2.8中,<:<,<%<和= =是什么意思?

我可以在Predef的API文档中看到它们是generics函数types(From)=> To的子类,但就是这么说。 呃,什么? 也许在某处有文档,但是search引擎不能像“<:<”那样处理“名称”,所以我一直无法find它。

后续问题:我应该什么时候使用这些时髦的符号/类,为什么?

这些被称为广义types约束 。 它们允许你从一个types参数化的类或者特征中进一步限制它的一个types参数。 这是一个例子:

 case class Foo[A](a:A) { // 'A' can be substituted with any type // getStringLength can only be used if this is a Foo[String] def getStringLength(implicit evidence: A =:= String) = a.length } 

隐含的参数evidence由编译器提供,如果AString 。 你可以把它看作是AString一个certificate – 这个论证本身并不重要,只知道它是存在的。 从技术上讲,它实际上是非常重要的,因为它代表了从AString的隐式转换,这就是允许你调用a.length而不是让编译器在你身上大喊大叫]

现在我可以像这样使用它:

 scala> Foo("blah").getStringLength res6: Int = 4 

但是,如果我尝试使用它包含除String以外的东西的Foo

 scala> Foo(123).getStringLength <console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String] 

你可以把这个错误读作“无法findInt == String的证据”……这是应该的! getStringLengthA的types施加了比Foo一般要求的更多的限制 ; 即只能在Foo[String]上调用getStringLength 。 这个约束是在编译时执行的,这很酷!

<:<<%<工作类似,但略有变化:

  • A =:= B表示A必须是B
  • A <:< B意味着A必须是B的子types(类似于简单types约束<:
  • A <%< B表示A必须视为B,可能通过隐式转换(类似于简单types约束<%

这个由@retronym构成的片段很好地解释了这种事情是如何完成的,以及泛化types约束现在如何变得简单。

附录

为了回答你的后续问题,我承认这个例子非常有意思,并不是很有用。 但是想象一下,使用它来定义类似于List.sumInts方法的东西,这个方法将整数列表相加。 你不想让这个方法在任何旧的List上被调用,只是一个List[Int] 。 但是Listtypes的构造函数不能被这样限制; 你仍然希望能够有string,foos,酒吧和whatnots列表。 因此,通过在sumInts上放置一个通用types约束,可以确保只有该方法有一个附加约束,它只能在List[Int] 。 基本上,你正在为特定types的列表编写特殊的代码。

不是一个完整的答案(其他人已经回答了这个问题),我只是想要注意以下几点,这可能有助于更好地理解语法:通常使用这些“操作符”的方式,例如pelotom的例子:

 def getStringLength(implicit evidence: A =:= String) 

为types操作符使用了Scala的替代中缀语法 。

所以, A =:= String=:=[A, String] (和=:=只是一个看起来很漂亮的类或特征)是一样的。 请注意,这个语法也适用于“常规”类,例如你可以写:

 val a: Tuple2[Int, String] = (1, "one") 

喜欢这个:

 val a: Int Tuple2 String = (1, "one") 

这与方法调用的两个语法类似,即“正常” .()以及操作符语法。

阅读其他答案,了解这些构造是什么。 这是你应该使用它们的时候。 只有在需要限制特定types的方法时才使用它们。

这是一个例子。 假设你想定义一个同质对,就像这样:

 class Pair[T](val first: T, val second: T) 

现在你想添加一个smaller的方法,像这样:

 def smaller = if (first < second) first else second 

这只有在T被订购时才有效。 你可以限制整个class级:

 class Pair[T <: Ordered[T]](val first: T, val second: T) 

但是,这似乎是一个耻辱 – 当T没有命令时,这个阶级可能会有用处。 对于types约束,您仍然可以定义smaller方法:

 def smaller(implicit ev: T <:< Ordered[T]) = if (first < second) first else second 

实例化一个Pair[File]只要你不调用它就可以了。

Option的情况下,实现者想要一个orNull方法,即使它对Option[Int]没有意义。 通过使用types约束,一切都很好。 你可以在Option[String]上使用orNull ,只要你不调用orNull就可以形成一个Option[Int]并使用它。 如果你尝试Some(42).orNull ,你会得到一个迷人的信息

  error: Cannot prove that Null <:< Int 

这取决于他们在哪里使用。 大多数情况下,在声明隐式参数types的时候,它们是类。 在罕见的情况下,它们也可能是对象。 最后,他们可以是Manifest对象上的操作符。 它们是在scala.Predef内部定义的。在前两种情况下,虽然没有特别好logging,

它们是为了提供一种方法来testing类之间的关系,就像<:<% do,在后者不能使用的情况下。

至于“我应该什么时候使用它们?”的问题,答案是你不应该,除非你知道你应该。 🙂 编辑 :好的,好的,这里有一些来自图书馆的例子。 Either ,你有:

 /** * Joins an <code>Either</code> through <code>Right</code>. */ def joinRight[A1 >: A, B1 >: B, C](implicit ev: B1 <:< Either[A1, C]): Either[A1, C] = this match { case Left(a) => Left(a) case Right(b) => b } /** * Joins an <code>Either</code> through <code>Left</code>. */ def joinLeft[A1 >: A, B1 >: B, C](implicit ev: A1 <:< Either[C, B1]): Either[C, B1] = this match { case Left(a) => a case Right(b) => Right(b) } 

Option ,你有:

 def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null 

你会发现一些其他的例子集合。