118

Scala 2.8 的新特性之一是上下文边界。什么是上下文绑定,它在哪里有用?

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

4

4 回答 4

154

Robert 的回答涵盖了 Context Bounds 的技术细节。我会给你我对它们含义的解释。

在 Scala 中,View Bound ( A <% B) 捕获了“可以被视为”的概念(而上限<:捕获了“is a”的概念)。上下文绑定 ( A : C) 表示“有一个”类型。您可以将有关清单的示例阅读为“T有一个Manifest”。您链接到 about Orderedvs的Ordering示例说明了差异。一个方法

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

表示参数可以看作是一个Ordered. 与之比较

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

这表示该参数具有关联的Ordering.

在使用方面,建立约定需要一段时间,但上下文边界优先于视图边界(视图边界现在已弃用)。一个建议是,当您需要将隐式定义从一个范围转移到另一个范围而不需要直接引用它时,最好使用上下文绑定( ClassManifest用于创建数组的情况肯定是这种情况)。

考虑视图边界和上下文边界的另一种方式是,第一个从调用者的范围传输隐式转换。第二个从调用者的范围传输隐式对象。

于 2010-06-06T06:51:59.730 回答
111

你找到这篇文章了吗?它在数组改进的上下文中涵盖了新的上下文绑定功能。

通常,具有上下文绑定的类型参数的形式为[T: Bound]; 它被扩展为普通类型参数T以及类型的隐式参数Bound[T]

考虑tabulate从 0 到给定长度的数字范围内应用给定函数 f 的结果形成数组的方法。直到 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
}

在 Scala 2.8 中,这不再可能,因为运行时信息对于创建正确的Array[T]. ClassManifest[T]需要通过将 a作为隐式参数传递给方法来提供此信息:

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
}
于 2010-06-05T22:08:14.687 回答
39

(这是一个括号注释。首先阅读并理解其他答案。)

上下文边界实际上概括了视图边界。

因此,鉴于此代码用 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 来表示,借助表示从 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$1: (T) => java.lang.String)Int

scala> f2(1)
res1: Int = 0

上下文绑定必须与 kind 的类型构造函数一起使用* => *。但是类型构造函数Function1是 kind (*, *) => *。类型别名的使用部分地将第二个类型参数与 type 一起应用String,从而产生正确类型的类型构造函数以用作上下文绑定。

有一个提议允许您在 Scala 中直接表达部分应用的类型,而无需在 trait 中使用类型别名。然后你可以写:

def f3[T : [X](X => String)](t: T) = 0 
于 2010-06-06T08:19:58.850 回答
19

这是另一个括号注释。

正如Ben 所指出的,上下文绑定表示类型参数和类型类之间的“具有”约束。换句话说,它表示存在特定类型类的隐式值的约束。

在使用上下文绑定时,通常需要显示该隐含值。例如,给定约束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) }
于 2010-12-18T02:15:48.703 回答