Scala 2.8 的新特性之一是上下文边界。什么是上下文绑定,它在哪里有用?
当然,我首先搜索(并找到了例如 this),但我找不到任何真正清晰和详细的信息。
Robert 的回答涵盖了 Context Bounds 的技术细节。我会给你我对它们含义的解释。
在 Scala 中,View Bound (A <% B
) 捕获了“can be seen as”的概念(而上限 <:
捕获了“is a”的概念)。上下文绑定 (A : C
) 表示“有一个”类型。您可以阅读有关清单的示例,因为“T
有一个 Manifest
”。您链接到的关于 Ordered
与 Ordering
的示例说明了差异。一个方法
def example[T <% Ordered[T]](param: T)
表示该参数可以看作是 Ordered
。与之比较
def example[T : Ordering](param: T)
表示该参数具有关联的 Ordering
。
在使用方面,建立约定需要一段时间,但上下文边界优先于视图边界 (view bounds are now deprecated)。一个建议是,当您需要将隐式定义从一个作用域转移到另一个作用域而不需要直接引用它时,首选上下文绑定(用于创建数组的 ClassManifest
肯定是这种情况)。
考虑视图边界和上下文边界的另一种方式是,第一个从调用者的范围传输隐式转换。第二个从调用者的范围传输隐式对象。
您找到 this article 了吗?它在数组改进的上下文中涵盖了新的上下文绑定功能。
通常,带有上下文绑定的类型参数的形式为[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]
作为隐式参数传递给方法来提供此信息:
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
}
(这是一个括号注释。首先阅读并理解其他答案。)
上下文边界实际上概括了视图边界。
因此,鉴于此代码用 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
这也可以用上下文绑定来表示,借助表示从类型 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
的第二个类型参数,从而产生了一个正确类型的类型构造函数以用作上下文绑定。
有一个提议允许您在 Scala 中直接表达部分应用的类型,而无需在 trait 中使用类型别名。然后你可以写:
def f3[T : [X](X => String)](t: T) = 0
To[String]
的类型成员 From
。我们不向 From
提供类型参数,因此我们指的是类型构造函数,而不是类型。此类型构造函数是用作上下文绑定的正确类型——* -> *
。这通过要求类型 To[String]#From[T]
的隐式参数来限制类型参数 T
。展开类型别名,瞧,剩下 Function1[String, T]
。
这是另一个括号注释。
如 Ben pointed out,上下文绑定表示类型参数和类型类之间的“具有”约束。换句话说,它表示存在特定类型类的隐式值的约束。
在使用上下文绑定时,通常需要显示该隐含值。例如,给定约束 T : Ordering
,通常需要满足约束的 Ordering[T]
实例。 As demonstrated here,可以使用 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) }
has a
对我来说更有意义]