什么是 Scala 中的“上下文绑定”?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2982276/
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
What is a "context bound" in Scala?
提问by Jesper
One of the new features of Scala 2.8 are context bounds. What is a context bound and where is it useful?
Scala 2.8 的新特性之一是上下文边界。什么是上下文绑定,它在哪里有用?
Of course I searched first (and found for example this) but I couldn't find any really clear and detailed information.
当然,我首先进行了搜索(并找到了例如this),但我找不到任何真正清晰和详细的信息。
采纳答案by Robert Harvey
Did you find this article? It covers the new context bound feature, within the context of array improvements.
你找到这篇文章了吗?它在数组改进的上下文中涵盖了新的上下文绑定功能。
Generally, a type parameter with a context boundis of the form [T: Bound]; it is expanded to plain type parameter Ttogether with an implicit parameter of type Bound[T].
通常,具有上下文绑定的类型参数的形式为[T: Bound]; 它与 typeT的隐式参数一起扩展为普通类型参数Bound[T]。
Consider the method tabulatewhich forms an array from the results of applying
a given function f on a range of numbers from 0 until a given length. Up to Scala 2.7, tabulate could be
written as follows:
考虑tabulate将给定函数 f 应用于从 0 到给定长度的数字范围的结果形成数组的方法。在 Scala 2.7 之前,表格可以写成如下:
def tabulate[T](len: Int, f: Int => T) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
In Scala 2.8 this is no longer possible, because runtime information is necessary to create the right representation of Array[T]. One needs to provide this information by passing a ClassManifest[T]into the method as an implicit parameter:
在 Scala 2.8 中,这不再可能,因为运行时信息对于创建Array[T]. 需要通过将 aClassManifest[T]作为隐式参数传递到方法中来提供此信息:
def tabulate[T](len: Int, f: Int => T)(implicit m: ClassManifest[T]) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
As a shorthand form, a context boundcan be used on the type parameter Tinstead, giving:
作为一种简写形式,可以在类型参数上使用上下文绑定T,例如:
def tabulate[T: ClassManifest](len: Int, f: Int => T) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}
回答by Ben Lings
Robert's answer covers the techinal details of Context Bounds. I'll give you my interpretation of their meaning.
罗伯特的回答涵盖了上下文边界的技术细节。我会给你我对它们含义的解释。
In Scala a View Bound (A <% B) captures the concept of 'can be seen as' (whereas an upper bound <:captures the concept of 'is a'). A context bound (A : C) says 'has a' about a type. You can read the examples about manifests as "Thas a Manifest". The example you linked to about Orderedvs Orderingillustrates the difference. A method
在 Scala 中,视图边界 ( A <% B) 捕获了“可以被视为”<:的概念(而上限捕获了“是 a”的概念)。上下文绑定 ( A : C) 表示“有一个”关于一个类型。您可以将有关清单的示例阅读为“T具有Manifest”。您链接到 about Orderedvs 的Ordering示例说明了差异。一个方法
def example[T <% Ordered[T]](param: T)
says that the parameter can be seen as an Ordered. Compare with
表示该参数可以被视为一个Ordered. 与之比较
def example[T : Ordering](param: T)
which says that the parameter has an associated Ordering.
这表示该参数具有关联的Ordering.
In terms of use, it took a while for conventions to be established, but context bounds are preferred over view bounds (view bounds are now deprecated). One suggestion is that a context bound is preferred when you need to transfer an implicit definition from one scope to another without needing to refer to it directly (this is certainly the case for the ClassManifestused to create an array).
在使用方面,约定的建立需要一段时间,但上下文边界优于视图边界(视图边界现在已弃用)。一个建议是,当您需要将隐式定义从一个作用域转移到另一个作用域而不需要直接引用它时,首选上下文绑定(这当然是 ClassManifest用于创建数组的情况)。
Another way of thinking about view bounds and context bounds is that the first transfers implicit conversions from the caller's scope. The second transfers implicit objects from the caller's scope.
考虑视图边界和上下文边界的另一种方式是首先从调用者的范围传输隐式转换。第二个从调用者的范围传输隐式对象。
回答by retronym
(This is a parenthetical note. Read and understand the other answers first.)
(这是一个括号注释。先阅读并理解其他答案。)
Context Bounds actually generalize View Bounds.
上下文边界实际上概括了视图边界。
So, given this code expressed with a View Bound:
因此,鉴于此代码用 View Bound 表示:
scala> implicit def int2str(i: Int): String = i.toString
int2str: (i: Int)String
scala> def f1[T <% String](t: T) = 0
f1: [T](t: T)(implicit evidence: (T) => String)Int
This could also be expressed with a Context Bound, with the help of a type alias representing functions from type Fto type T.
这也可以用 Context Bound 来表示,在类型别名的帮助下表示从 typeF到 type 的函数T。
scala> trait To[T] { type From[F] = F => T }
defined trait To
scala> def f2[T : To[String]#From](t: T) = 0
f2: [T](t: T)(implicit evidence: (T) => java.lang.String)Int
scala> f2(1)
res1: Int = 0
A context bound must be used with a type constructor of kind * => *. However the type constructor Function1is of kind (*, *) => *. The use of the type alias partially applies second type parameter with the type String, yielding a type constructor of the correct kind for use as a context bound.
上下文绑定必须与 kind 的类型构造函数一起使用* => *。然而,类型构造函数Function1是一种(*, *) => *。类型别名的使用部分地将第二个类型参数应用于 type String,产生一个正确类型的类型构造函数用作上下文绑定。
There is a proposal to allow you to directly express partially applied types in Scala, without the use of the type alias inside a trait. You could then write:
有一个提议允许您在 Scala 中直接表达部分应用的类型,而无需在 trait 中使用类型别名。然后你可以写:
def f3[T : [X](X => String)](t: T) = 0
回答by Aaron Novstrup
This is another parenthetical note.
这是另一个括号注释。
As Ben pointed out, a context bound represents a "has-a" constraint between a type parameter and a type class. Put another way, it represents a constraint that an implicit value of a particular type class exists.
正如Ben 指出的那样,上下文绑定表示类型参数和类型类之间的“具有”约束。换句话说,它表示存在特定类型类的隐式值的约束。
When utilizing a context bound, one often needs to surface that implicit value. For example, given the constraint T : Ordering, one will often need the instance of Ordering[T]that satisfies the constraint. As demonstrated here, it's possible to access the implicit value by using the implicitlymethod or a slightly more helpful contextmethod:
在使用上下文绑定时,通常需要显示该隐式值。例如,给定约束T : Ordering,人们通常需要Ordering[T]满足约束的实例。 如此处所示,可以通过使用implicitly方法或更有用的方法来访问隐式值context:
def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) =
xs zip ys map { t => implicitly[Numeric[T]].times(t._1, t._2) }
or
或者
def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) =
xs zip ys map { t => context[T]().times(t._1, t._2) }

