ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

下面是一个相关问题,它至少可以部分回答您的问题:stackoverflow.com/questions/2603003/operator-in-scala
symbolhound.com 是您的代码搜索朋友 :)
Haskell 的typeclass是否执行这些运算符的工作?示例:compare :: Ord a => a -> a -> Ordering?我试图从它的 Haskell 对应部分来理解这个 Scala 概念。
这可能有助于理解运算符 =:=, stackoverflow.com/questions/67773938/…

T
Tom Crockett

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

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。这个约束是在编译时强制执行的,这很酷!

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

A =:= B 意味着 A 必须是 B

A <:< B 表示 A 必须是 B 的子类型(类似于简单类型约束 <:)

A <%< B 意味着 A 必须作为 B 可见,可能通过隐式转换(类似于简单类型约束 <%)

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

附录

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


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

不是一个完整的答案(其他人已经回答了这个问题),我只想注意以下几点,这可能有助于更好地理解语法:您通常使用这些“运算符”的方式,例如在 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. 完全错过了这个解释,否则整个事情就没有意义
T
Tom Crockett

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

这是一个例子。假设您要定义一个同质的 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

我意识到这是在这个答案之后的几年,但我正在寻找 <:< 的用例,并且我认为 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
D
Daniel C. Sobral

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

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

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

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


:-) 是其中之一吗?我同意你对“我什么时候应该使用它们?”的回答。适用于很多事情。
“它们旨在提供一种测试类之间关系的方法”<-太笼统而无济于事
“至于‘我什么时候应该使用它们?’的问题,答案是你不应该,除非你知道你应该。” <--这就是我问的原因。我希望自己能够做出这样的决定。