ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

这个出色的答案比较/对比了上下文边界和视图边界:stackoverflow.com/questions/4465948/…
这是一个非常好的答案stackoverflow.com/a/25250693/1586965

b
bbarker

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

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

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

表示该参数可以看作是 Ordered。与之比较

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

表示该参数具有关联的 Ordering

在使用方面,建立约定需要一段时间,但上下文边界优先于视图边界 (view bounds are now deprecated)。一个建议是,当您需要将隐式定义从一个作用域转移到另一个作用域而不需要直接引用它时,首选上下文绑定(用于创建数组的 ClassManifest 肯定是这种情况)。

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


“有一个”而不是“是一个”或“被视为”是我的关键见解 - 在任何其他解释中都没有看到这一点。拥有原本略显神秘的运算符/函数的简明英文版本使其更容易吸收 - 谢谢!
@Ben Lings 你是什么意思......'有'关于一种类型......?类型是什么?
@jhegedus 这是我的解析:“关于类型”意味着 A 指的是一种类型。短语“has a”经常用于面向对象的设计中来描述对象关系(例如,客户“有一个”地址)。但是这里的“有”关系是类型之间的,而不是对象之间的。这是一个松散的类比,因为“具有”关系不像 OO 设计中那样固有或普遍。客户总是有一个地址,但对于上下文绑定,A 并不总是有一个 C。相反,上下文绑定指定必须隐式提供 C[A] 的实例。
我学习Scala已经一个月了,这是我这个月看到的最好的解释!谢谢@Ben!
@Ben Lings:谢谢,在花了这么长时间了解什么是上下文绑定之后,您的回答非常有帮助。[has a 对我来说更有意义]
R
Robert Harvey

您找到 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
}

r
retronym

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

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

因此,鉴于此代码用 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 

你能解释一下f2定义中#From的含义吗?我不确定 F 型是在哪里构造的(我说得对吗?)
它称为类型投影,引用类型 To[String] 的类型成员 From。我们不向 From 提供类型参数,因此我们指的是类型构造函数,而不是类型。此类型构造函数是用作上下文绑定的正确类型——* -> *。这通过要求类型 To[String]#From[T] 的隐式参数来限制类型参数 T。展开类型别名,瞧,剩下 Function1[String, T]
那应该是 Function1[T, String] 吗?
C
Community

这是另一个括号注释。

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) }