什么是 Scala 中的“上下文绑定”?

Scala 2.8的一个新特性是上下文界限。什么是上下文约束,它在哪里有用?

当然,我首先搜索(并找到例如 这个) ,但我无法找到任何真正清晰和详细的信息。

33543 次浏览

你找到 this article了吗? 它涵盖了新的上下文绑定特性,在数组改进的上下文中。

一般来说,具有 与上下文有关的类型参数的形式为 [T: Bound]; 它被扩展为普通类型参数 T以及类型为 Bound[T]的隐式参数。

考虑方法 tabulate,它根据应用程序的结果形成一个数组 从0到给定长度范围内的给定函数 f。直到 Scala 2.7,表格可以是 written as follows:

def tabulate[T](len: Int, f: Int => T) = {
val xs = new Array[T](len)
for (i <- 0 until len) xs(i) = f(i)
xs
}

在 Scala 2.8中,这已经不可能了,因为创建正确的 Array[T]表示需要运行时信息。需要通过将 ClassManifest[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
}

作为一种简写形式,可以在类型参数 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
}

罗伯特的答案涵盖了上下文界限的技术细节。我将给你我对其含义的解释。

在 Scala 中,视图绑定(View Bound,A <% B)捕获了“可以看作”的概念(而上限 <:捕获了“ is a”的概念)。上下文绑定(A : C)表示关于某种类型的“ has a”。您可以通过“ T有一个 Manifest”来阅读关于清单的示例。您链接到的关于 OrderedOrdering的示例说明了这种差异。一种方法

def example[T <% Ordered[T]](param: T)

表示参数可以看作是 Ordered

def example[T : Ordering](param: T)

表示参数有一个关联的 Ordering

在使用方面,建立约定需要一段时间,但是上下文界限优先于视图界限(现在不推荐使用视图边界)。一种建议是,当您需要将隐式定义从一个范围转移到另一个范围而不需要直接引用它时,首选上下文绑定(对于用于创建数组的 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.

(这是附加说明,请先阅读并理解其他答案。)

上下文界限实际上泛化视图界限。

So, given this code expressed with a 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$1: (T) => String)Int

这也可以通过上下文绑定(Context Bound)表示,并借助表示从 F类型到 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$1: (T) => java.lang.String)Int


scala> f2(1)
res1: Int = 0

上下文绑定必须与某种类型的型别构造器 * => *一起使用。然而,型别构造器的 Function1是类似于 (*, *) => *的。类型别名的使用部分地应用了类型 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:

def f3[T : [X](X => String)](t: T) = 0

这是另一个附加说明。

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.

当使用上下文绑定时,通常需要显示该隐式值。例如,给定约束 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) }

或者

def **[T : Numeric](xs: Iterable[T], ys: Iterable[T]) =
xs zip ys map { t => context[T]().times(t._1, t._2) }