我可以在Predef的API文档中看到它们是泛型函数类型(From)=> To的子类,仅此而已。嗯什么?也许某处有文档,但是搜索引擎不能很好地处理“ <:<”之类的“名称”,因此我找不到它。
后续问题:我什么时候应该使用这些时髦的符号/类,为什么?
typeclass
ES是否执行这些操作员的工作?例如:compare :: Ord a => a -> a -> Ordering
?我正在尝试就其Haskell对应部分理解这种Scala概念。
我可以在Predef的API文档中看到它们是泛型函数类型(From)=> To的子类,仅此而已。嗯什么?也许某处有文档,但是搜索引擎不能很好地处理“ <:<”之类的“名称”,因此我找不到它。
后续问题:我什么时候应该使用这些时髦的符号/类,为什么?
typeclass
ES是否执行这些操作员的工作?例如:compare :: Ord a => a -> a -> Ordering
?我正在尝试就其Haskell对应部分理解这种Scala概念。
Answers:
这些称为广义类型约束。它们允许您从类型参数化的类或特征中进一步限制其类型参数之一。这是一个例子:
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
由编译器提供,iff A
is String
。你可以把它作为一个证据即A
是String
--the说法本身并不重要,只知道它的存在。[编辑:好吧,从技术上讲,它实际上很重要,因为它表示从A
到的隐式转换String
,这使您可以调用a.length
而不会让编译器大吼大叫]
现在,我可以像这样使用它:
scala> Foo("blah").getStringLength
res6: Int = 4
但是,如果我尝试将其与Foo
包含非的内容一起使用String
:
scala> Foo(123).getStringLength
<console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String]
您可以将错误读取为“找不到证据,证明Int ==字符串”……就是这样!getStringLength
对类型的限制A
超出了Foo
一般要求;即,您只能getStringLength
在上调用Foo[String]
。在编译时强制执行此约束,这很酷!
<:<
和<%<
工作类似,但略有不同:
A =:= B
表示A必须正好是BA <:< B
意味着A必须是B的子类型(类似于简单类型约束<:
)A <%< B
装置A必须是可见的作为B,可能经由隐式转换(类似于简单类型约束<%
)@retronym的这段代码很好地解释了这种事情过去是如何实现的,以及通用类型约束如何使现在变得更容易。
附录
为了回答您的后续问题,坦率地说,我给出的示例是人为设计的,显然没有用。但是,想象一下使用它来定义类似List.sumInts
方法的方法,该方法会累加一个整数列表。您不希望在任何旧版本List
(仅一个)上调用此方法List[Int]
。但是,List
类型构造函数不能如此受约束;您仍然希望能够拥有字符串,foos,bar和whatnot的列表。因此,通过对放置通用类型约束sumInts
,可以确保仅该方法具有只能在上使用的附加约束List[Int]
。本质上,您正在为某些类型的列表编写特殊情况的代码。
Manifest
,您还没有提到同名的方法。
Manifest
是<:<
和>:>
只有...因为OP提到的正是3个品种的广义类型约束,我假定这就是他感兴趣的
class =:=[From, To] extends From => To
,这意味着type的隐式值From =:= To
实际上是从到的隐式转换。因此,通过接受类型为隐式的参数,您可以将其隐式转换为。如果您更改顺序并将隐式参数设置为type ,则它将不起作用,因为这将是从到的隐式转换。From
To
A =:= String
A
String
String =:= A
String
A
From =:= To
在范围内具有类型的隐式值意味着您具有隐式转换From => To
,但是隐含的含义并不反向。有一个隐式转换A => B
并没有暗示你有一个实例A =:= B
。=:=
是在中定义的密封抽象类scala.Predef
,并且只有一个公开暴露的实例,该实例是隐式的,类型为A =:= A
。因此,您可以确保type的隐式值A =:= B
见证A
and B
是相等的事实。
不是一个完整的答案(其他人已经回答了这个问题),我只想注意以下内容,这可能有助于更好地理解语法:通常使用这些“运算符”的方式,例如在pelotom的示例中:
def getStringLength(implicit evidence: A =:= String)
因此,A =:= String
与相同=:=[A, String]
(=:=
只是一个具有花哨名称的类或特征)。请注意,此语法也适用于“常规”类,例如,您可以编写:
val a: Tuple2[Int, String] = (1, "one")
像这样:
val a: Int Tuple2 String = (1, "one")
它与方法调用的两种语法相似,即“ .
and” ()
和“运算符”语法。
makes use of Scala's alternative infix syntax for type operators.
完全错过了这个解释,否则,整个事情将变得毫无意义
阅读其他答案以了解这些构造是什么。这是当你应该使用它们。仅在需要限制特定类型的方法时使用它们。
这是一个例子。假设您要定义一个同类对,如下所示:
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
可以实例化一个a Pair[File]
,只要您不调用 smaller
它就可以。
在的情况下Option
,实现者需要一个orNull
方法,即使该方法对于而言没有意义Option[Int]
。通过使用类型约束,一切都很好。您可以orNull
在上使用Option[String]
,也可以形成一个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
。
这取决于它们的使用位置。通常,在声明隐式参数类型时使用它们是类。在极少数情况下,它们也可能是对象。最后,它们可以是Manifest
对象的运算符。scala.Predef
在前两种情况中,它们是在内部定义的,尽管没有特别好的文档说明。
它们旨在提供一种方法来测试类之间的关系,就像<:
并且<%
不能在无法使用后者的情况下一样。
至于“我什么时候应该使用它们?”这个问题,答案是你不应该,除非你知道自己应该使用。:-) 编辑:好的,好的,这是库中的一些示例。在上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
您还会在集合中找到其他示例。
:-)
其中之一吗?我同意您对“何时应使用它们?”的回答。适用于很多东西。