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

Jes*_*per 108 scala scala-2.8 context-bound

Scala 2.8的一个新功能是上下文边界.什么是上下文绑定以及它在哪里有用?

当然我先搜索(例如找到这个),但我找不到任何非常清晰和详细的信息.

Ben*_*ngs 138

Robert的答案涵盖了Context Bounds的技术细节.我会告诉你我们对它们意义的解释.

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

def example[T <% Ordered[T]](param: T)
Run Code Online (Sandbox Code Playgroud)

说参数可以看作是Ordered.与之比较

def example[T : Ordering](param: T)
Run Code Online (Sandbox Code Playgroud)

这表示参数有关联Ordering.

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

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

  • "有一个"而不是"是一个"或"被视为"是我的关键见解 - 在其他任何解释中都没有看到这一点.有一个简单的英文版本的其他稍微神秘的操作员/功能使其更容易吸收 - 谢谢! (2认同)

Rob*_*vey 102

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

通常,具有上下文绑定的类型参数具有以下形式[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
}
Run Code Online (Sandbox Code Playgroud)

在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
}
Run Code Online (Sandbox Code Playgroud)

作为简写形式,可以在类型参数上使用上下文绑定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
}
Run Code Online (Sandbox Code Playgroud)


ret*_*nym 38

(这是一个括号内容.请先阅读并理解其他答案.)

Context Bounds实际上概括了View Bounds.

所以,鉴于这个代码用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
Run Code Online (Sandbox Code Playgroud)

这也可以用Context Bound表示,借助于表示从type F到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
Run Code Online (Sandbox Code Playgroud)

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

有一个建议允许您直接在Scala中表达部分应用的类型,而不使用特征中的类型别名.然后你可以写:

def f3[T : [X](X => String)](t: T) = 0 
Run Code Online (Sandbox Code Playgroud)


Aar*_*rup 17

这是另一个括号内容.

正如Ben所指出的,上下文绑定表示类型参数和类型类之间的"has-a"约束.换句话说,它表示存在特定类型类的隐式值的约束.

在利用上下文绑定时,通常需要表达隐式值.例如,给定约束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) }
Run Code Online (Sandbox Code Playgroud)

要么

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