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

Posted

技术标签:

【中文标题】<:<、<%< 和 =:= 在 Scala 2.8 中是啥意思,它们在哪里记录?【英文标题】:What do <:<, <%<, and =:= mean in Scala 2.8, and where are they documented?<:<、<%< 和 =:= 在 Scala 2.8 中是什么意思,它们在哪里记录? 【发布时间】:2012-07-12 23:29:45 【问题描述】:

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

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

【问题讨论】:

这里有一个相关问题,可以至少部分回答您的问题:***.com/questions/2603003/operator-in-scala symbolhound.com 是你的代码搜索朋友 :) Haskell 的typeclasses 能完成这些操作员的工作吗?示例:compare :: Ord a =&gt; a -&gt; a -&gt; Ordering?我正在尝试从它的 Haskell 对应部分来理解这个 Scala 概念。 这可能有助于理解运算符 =:=, ***.com/questions/67773938/… 【参考方案1】:

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

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证明——参数本身并不重要,只知道它存在。 [编辑:嗯,从技术上讲,它实际上很重要,因为它表示从 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]

您可以将该错误理解为“无法找到 Int == String 的证据”......应该是这样! getStringLengthA 的类型施加更多限制,而不是Foo 通常要求的;也就是说,您只能在Foo[String] 上调用getStringLength。这个约束是在编译时强制执行的,这很酷!

&lt;:&lt;&lt;%&lt; 工作方式相似,但略有不同:

A =:= B 表示 A 必须是 B A &lt;:&lt; B 表示 A 必须是 B 的子类型(类似于 simple 类型约束 &lt;:A &lt;%&lt; B 表示 A 必须像 B 一样可见,可能通过隐式转换(类似于简单类型约束 &lt;%

@retronym 的This snippet 很好地解释了这种事情过去是如何完成的,以及通用类型约束如何使它变得更容易。

附录

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

【讨论】:

好吧,但是Manifest 上也有同名的方法,你没有提到。 Manifest 上的方法只有 &lt;:&lt;&gt;:&gt;... 因为 OP 准确地提到了 3 种广义类型约束,我假设这就是他感兴趣的。 @IttayD:很聪明...class =:=[From, To] extends From =&gt; To,这意味着From =:= To 类型的隐式值实际上是从From 到@ 的隐式转换 987654360@。因此,通过接受A =:= String 类型的隐式参数,您是在说A 可以隐式转换为String。如果您更改了顺序并将隐式参数设为String =:= A 类型,则它将不起作用,因为这将是从StringA 的隐式转换。 那三个字符的符号有名字吗?我对 Scala 的符号汤的问题是它们很难口头谈论,而且几乎不可能使用 Google 或任何其他搜索引擎来找到它们的使用讨论和示例。 @Andrea 不,这仅在类型完全相等时才有效。请注意,我说过在范围内具有 From =:= To 类型的隐式值意味着您有一个隐式转换 From =&gt; To,但其含义不会倒退;有一个隐式转换A =&gt; B 确实 暗示你有一个A =:= B 的实例。 =:=scala.Predef中定义的密封抽象类,只有一个公开暴露的实例,它是隐式的,类型为A =:= A。因此,您可以保证 A =:= B 类型的隐式值见证了 AB 相等的事实。【参考方案2】:

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

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

使用 Scala 的替代方案 infix syntax for type operators。

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

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

像这样:

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

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

【讨论】:

需要投票,因为makes use of Scala's alternative infix syntax for type operators. 完全错过了这个解释,否则整个事情就没有意义【参考方案3】:

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

这是一个例子。假设你想定义一个同构 Pair,像这样:

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

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

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

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

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

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

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

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

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

 error: Cannot prove that Null <:< Int

【讨论】:

我意识到这是在这个答案之后多年,但我正在寻找&lt;:&lt; 的用例,我认为Ordered 示例不再那么引人注目,因为现在你宁愿使用Ordering 类型类而不是 Ordered 特征。比如:def smaller(implicit ord: Ordering[T]) = if (ord.lt(first, second)) first else second. @ebruchez:一个用例是在未修改的 scala 中编码联合类型,请参阅 milessabin.com/blog/2011/06/09/scala-union-types-curry-howard【参考方案4】:

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

它们旨在提供一种测试类之间关系的方法,就像 &lt;:&lt;% 在后者无法使用的情况下所做的那样。

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

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

【讨论】:

:-) 是其中一个吗?我同意你对“我什么时候应该使用它们?”的回答。适用于很多事情。 “它们的目的是提供一种测试类之间关系的方法” “至于“我什么时候应该使用它们?”的问题,答案是你不应该,除非你知道你应该。”

以上是关于<:<、<%< 和 =:= 在 Scala 2.8 中是啥意思,它们在哪里记录?的主要内容,如果未能解决你的问题,请参考以下文章

<<= 和 |= 的含义

“cat > filename << EOF”和“cat << EOF > filename”是不是等效? [复制]

Consule作为注册中心配置实例

标签 <q>、<abbr>、<defn>、<ins>、<del> 和 <s> 的正确用法是啥? [关闭]

Rc<RefCell<T>> 和 RefCell<Rc<T>> 有啥区别?

Rc<RefCell<T>> 和 RefCell<Rc<T>> 有啥区别?