gpt4 book ai didi

scala - 什么是 Scala 上下文和 View 边界?

转载 作者:行者123 更新时间:2023-12-03 04:04:57 24 4
gpt4 key购买 nike

简单来说,上下文和 View 边界是什么,它们之间有什么区别?

一些易于遵循的示例也很棒!

最佳答案

我认为这已经被问到了,但是,如果是这样,这个问题在“相关”栏中并不明显。所以,这里是:

什么是 View 边界?

View 边界是 Scala 中引入的一种机制,用于启用某种类型 A好像是某种类型 B .典型的语法是这样的:

def f[A <% B](a: A) = a.bMethod

换句话说, A应该隐式转换为 B可用,以便人们可以拨打 B类型对象的方法 A .标准库中 View 边界的最常见用法(无论如何在 Scala 2.8.0 之前)是 Ordered , 像这样:
def f[A <% Ordered[A]](a: A, b: A) = if (a < b) a else b

因为可以转换 AOrdered[A] ,并且因为 Ordered[A]定义方法 <(other: A): Boolean , 我可以使用表达式 a < b .

请注意 view bounds are deprecated ,你应该避免它们。

什么是上下文绑定(bind)?

上下文边界是在 Scala 2.8.0 中引入的,通常与所谓的类型类模式一起使用,这是一种模拟 Haskell 类型类提供的功能的代码模式,但方式更为冗长。

虽然 View 绑定(bind)可以用于简单类型(例如, A <% String ),但上下文绑定(bind)需要参数化类型,例如 Ordered[A]以上,但不同于 String .

上下文绑定(bind)描述的是隐式值,而不是 View 绑定(bind)的隐式转换。它用于声明对于某些类型 A ,有一个类型为 B[A] 的隐式值可用的。语法如下:
def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A]

这比 View 边界更令人困惑,因为它并不清楚如何使用它。 Scala 中常见的用法示例如下:
def f[A : ClassManifest](n: Int) = new Array[A](n)

Array参数化类型的初始化需要 ClassManifest可用,因为与类型删除和数组的非删除性质相关的神秘原因。

库中另一个非常常见的例子有点复杂:
def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)

在这里, implicitly用于检索我们想要的隐式值,类型之一 Ordering[A] , 哪个类定义了方法 compare(a: A, b: A): Int .

我们将在下面看到另一种方法。

View 边界和上下文边界是如何实现的?

考虑到它们的定义, View 边界和上下文边界都是使用隐式参数实现的,这不足为奇。实际上,我展示的语法是真正发生的事情的语法糖。看看下面他们是如何脱糖的:
def f[A <% B](a: A) = a.bMethod
def f[A](a: A)(implicit ev: A => B) = a.bMethod

def g[A : B](a: A) = h(a)
def g[A](a: A)(implicit ev: B[A]) = h(a)

因此,很自然地,人们可以用完整的语法编写它们,这对于上下文边界特别有用:
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = ord.compare(a, b)

View 边界有什么用?

View 边界主要用于利用我的库模式的皮条客模式,在您想以某种方式返回原始类型的情况下,通过该模式向现有类“添加”方法。如果您不需要以任何方式返回该类型,那么您就不需要 View 绑定(bind)。

View 绑定(bind)使用的经典示例是处理 Ordered .请注意 Int不是 Ordered ,例如,虽然有一个隐式转换。前面给出的示例需要一个 View 边界,因为它返回未转换的类型:
def f[A <% Ordered[A]](a: A, b: A): A = if (a < b) a else b

如果没有 View 边界,此示例将无法工作。但是,如果我要返回另一种类型,则不再需要 View 绑定(bind):
def f[A](a: Ordered[A], b: A): Boolean = a < b

此处的转换(如果需要)发生在我将参数传递给 f 之前, 所以 f不需要知道它。

除了 Ordered ,库中最常见的用法是处理 StringArray ,它们是 Java 类,就像它们是 Scala 集合一样。例如:
def f[CC <% Traversable[_]](a: CC, b: CC): CC = if (a.size < b.size) a else b

如果试图在没有 View 边界的情况下执行此操作,则返回类型 String将是 WrappedString (Scala 2.8),同样适用于 Array .

即使该类型仅用作返回类型的类型参数,也会发生同样的事情:
def f[A <% Ordered[A]](xs: A*): Seq[A] = xs.toSeq.sorted

上下文边界有什么用?

上下文边界主要用于所谓的类型类模式,作为对 Haskell 类型类的引用。基本上,该模式通过一种隐式适配器模式使功能可用,从而实现了继承的替代方案。

经典的例子是 Scala 2.8 的 Ordering ,取代了 Ordered贯穿 Scala 的图书馆。用法是:
def f[A : Ordering](a: A, b: A) = if (implicitly[Ordering[A]].lt(a, b)) a else b

虽然你通常会看到这样写:
def f[A](a: A, b: A)(implicit ord: Ordering[A]) = {
import ord.mkOrderingOps
if (a < b) a else b
}

其中利用了 Ordering 中的一些隐式转换支持传统的运算符(operator)风格。 Scala 2.8 中的另一个示例是 Numeric :
def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b)

一个更复杂的例子是 CanBuildFrom 的新集合用法。 ,但是已经有一个很长的答案了,所以我会在这里避免它。而且,如前所述,还有 ClassManifest用法,这是初始化没有具体类型的新数组所必需的。

与 typeclass 模式绑定(bind)的上下文更有可能被您自己的类使用,因为它们可以实现关注点分离,而通过良好的设计可以在您自己的代码中避免 View 边界(它主要用于绕过其他人的设计) )。

尽管这已经有很长一段时间了,但上下文边界的使用在 2010 年才真正开始流行,现在在 Scala 的大多数最重要的库和框架中都在某种程度上有所体现。然而,最极端的例子是 Scalaz 库,它为 Scala 带来了 Haskell 的许多强大功能。我建议阅读 typeclass 模式以更熟悉它的所有使用方式。

编辑

感兴趣的相关问题:
  • A discussion on types, origin and precedence of implicits
  • Chaining implicits
  • 关于scala - 什么是 Scala 上下文和 View 边界?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4465948/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com