<:<、<%< 和 =:= 在 Scala 2.8 中是什么意思,它们在哪里记录?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3427345/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:20:39  来源:igfitidea点击:

What do <:<, <%<, and =:= mean in Scala 2.8, and where are they documented?

scalascala-2.8type-constraints

提问by Jeff

I can see in the API docs for Predefthat they're subclasses of a generic function type (From) => To, but that's all it says. Um, what? Maybe there's documentation somewhere, but search engines don't handle "names" like "<:<" very well, so I haven't been able to find it.

我可以在Predef的 API 文档中看到它们是通用函数类型 (From) => To 的子类,但仅此而已。嗯什么?也许某处有文档,但搜索引擎不能很好地处理“<:<”之类的“名称”,所以我一直没能找到它。

Follow-up question: when should I use these funky symbols/classes, and why?

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

回答by Tom Crockett

These are called generalized type constraints. They allow you, from within a type-parameterized class or trait, to further constrainone of its type parameters. Here's an example:

这些被称为广义类型约束。它们允许您从类型参数化的类或特征中进一步约束其类型参数之一。下面是一个例子:

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
}

The implicit argument evidenceis supplied by the compiler, iff Ais String. You can think of it as a proofthat Ais String--the argument itself isn't important, only knowing that it exists. [edit: well, technically it actually is important because it represents an implicit conversion from Ato String, which is what allows you to call a.lengthand not have the compiler yell at you]

隐式参数evidence由编译器提供, iff Ais String。你可以把它作为一个证据AString--the说法本身并不重要,只知道它的存在。[编辑:嗯,从技术上讲,它实际上很重要,因为它代表了从Ato的隐式转换String,这使您可以调用a.length而不会让编译器对您大喊大叫]

Now I can use it like so:

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

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

But if I tried use it with a Foocontaining something other than a String:

但是,如果我尝试将它与Foo包含 a 以外的内容一起使用String

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

You can read that error as "could not find evidence that Int == String"... that's as it should be! getStringLengthis imposing further restrictionson the type of Athan what Fooin general requires; namely, you can only invoke getStringLengthon a Foo[String]. This constraint is enforced at compile-time, which is cool!

您可以将该错误解读为“找不到 Int == String 的证据”……应该是这样!getStringLength正在对超出一般要求的类型施加进一步的限制;也就是说,您只能在 a 上调用。这个约束是在编译时强制执行的,这很酷!AFoogetStringLengthFoo[String]

<:<and <%<work similarly, but with slight variations:

<:<并以<%<类似的方式工作,但略有不同:

  • A =:= Bmeans A must be exactly B
  • A <:< Bmeans A must be a subtype of B (analogous to the simpletype constraint <:)
  • A <%< Bmeans A must be viewableas B, possibly via implicit conversion (analogous to the simple type constraint <%)
  • A =:= B意味着 A 必须正好是 B
  • A <:< B意味着 A 必须是 B 的子类型(类似于简单类型约束<:
  • A <%< B意味着 A 必须视为 B,可能通过隐式转换(类似于简单类型约束<%

This snippetby @retronym is a good explanation of how this sort of thing used to be accomplished and how generalized type constraints make it easier now.

@retronym 的这个片段很好地解释了过去如何完成这类事情以及通用类型约束如何使现在变得更容易。

ADDENDUM

附录

To answer your follow-up question, admittedly the example I gave is pretty contrived and not obviously useful. But imagine using it to define something like a List.sumIntsmethod, which adds up a list of integers. You don't want to allow this method to be invoked on any old List, just a List[Int]. However the Listtype constructor can't be so constrainted; you still want to be able to have lists of strings, foos, bars, and whatnots. So by placing a generalized type constraint on sumInts, you can ensure that just that methodhas an additional constraint that it can only be used on a List[Int]. Essentially you're writing special-case code for certain kinds of lists.

为了回答您的后续问题,诚然,我给出的示例非常做作,显然没有用。但是想象一下使用它来定义类似List.sumInts方法的东西,它将整数列表相加。您不想允许在任何旧ListList[Int]. 但是List类型构造器不能这么约束;您仍然希望能够拥有字符串、foos、bars 和诸如此类的列表。因此,通过在 上放置一个通用类型约束sumInts,您可以确保该方法具有一个附加约束,即它只能在 上使用List[Int]。本质上,您正在为某些类型的列表编写特殊情况代码。

回答by Jesper

Not a complete answer (others have already answered this), I just wanted to note the following, which maybe helps to understand the syntax better: The way you normally use these "operators", as for example in pelotom's example:

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

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

makes use of Scala's alternative infix syntax for type operators.

使用 Scala 的替代中缀语法来处理类型操作符

So, A =:= Stringis the same as =:=[A, String](and =:=is just a class or trait with a fancy-looking name). Note that this syntax also works with "regular" classes, for example you can write:

所以,A =:= String=:=[A, String](并且=:=只是一个具有花哨名称的类或特征)相同。请注意,此语法也适用于“常规”类,例如您可以编写:

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

like this:

像这样:

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

It's similar to the two syntaxes for method calls, the "normal" with .and ()and the operator syntax.

它类似于方法调用的两种语法,“普通” with .and()和运算符语法。

回答by cayhorstmann

Read the other answers to understand what these constructs are. Here is whenyou should use them. You use them when you need to constrain a method for specific types only.

阅读其他答案以了解这些结构是什么。这是你应该使用它们。当您只需要限制特定类型的方法时,您可以使用它们。

Here is an example. Suppose you want to define a homogeneous Pair, like this:

这是一个例子。假设您要定义一个同构 Pair,如下所示:

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

Now you want to add a method smaller, like this:

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

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

That only works if Tis ordered. You could restrict the entire class:

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

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

But that seems a shame--there could be uses for the class when Tisn't ordered. With a type constraint, you can still define the smallermethod:

但这似乎是一种耻辱——当T没有被订购时,课程可能会有用。使用类型约束,您仍然可以定义smaller方法:

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

It's ok to instantiate, say, a Pair[File], as long as you don't callsmalleron it.

可以实例化 a Pair[File]只要您不调用smaller它。

In the case of Option, the implementors wanted an orNullmethod, even though it doesn't make sense for Option[Int]. By using a type constraint, all is well. You can use orNullon an Option[String], and you can form an Option[Int]and use it, as long as you don't call orNullon it. If you try Some(42).orNull, you get the charming message

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

 error: Cannot prove that Null <:< Int

回答by Daniel C. Sobral

It depends on where they are being used. Most often, when used while declaring types of implicit parameters, they are classes. They can be objects too in rare instances. Finally, they can be operators on Manifestobjects. They are defined inside scala.Predefin the first two cases, though not particularly well documented.

这取决于它们的使用位置。大多数情况下,在声明隐式参数类型时使用时,它们是类。在极少数情况下,它们也可以是对象。最后,它们可以是Manifest对象的运算符。它们scala.Predef在前两种情况下在内部定义,尽管没有特别详细的记录。

They are meant to provide a way to test the relationship between the classes, just like <:and <%do, in situations when the latter cannot be used.

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

As for the question "when should I use them?", the answer is you shouldn't, unless you know you should. :-) EDIT: Ok, ok, here are some examples from the library. On Either, you have:

至于“我什么时候应该使用它们?”的问题,答案是你不应该,除非你知道你应该。:-)编辑:好的,好的,这里有一些来自图书馆的例子。在 上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)
 }

On Option, you have:

在 上Option,您有:

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

You'll find some other examples on the collections.

您会在集合中找到一些其他示例。