什么是Scala上下文和视图边界?

简而言之,上下文和观点界限是什么?它们之间有什么区别?

一些容易遵循的例子也会很棒!

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

什么是视图绑定?

视图绑定是Scala中引入的一种机制,可以使用某些typesA ,就好像它是某种typesB 。 典型的语法是这样的:

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

换句话说, A应该有一个隐式转换为可用的B ,以便可以在Atypes的对象上调用B方法。 在标准库(Scala 2.8.0之前)中,最常见的使用视图边界是Ordered ,就像这样:

 def f[A <% Ordered[A]](a: A, b: A) = if (a < b) a else b 

因为可以将A转换为Ordered[A] ,并且由于Ordered[A]定义了方法<(other: A): Boolean ,所以可以使用expression式a < b

请注意, 查看范围已被弃用 ,您应该避免它们。

什么是上下文绑定?

Scala 2.8.0中引入了上下文边界,并且通常与所谓的types模式 (一种模仿Haskelltypes类提供的function的代码模式 )一起使用,尽pipe以更详细的方式。

虽然视图绑定可以用于简单types(例如, A <% String ),但上下文绑定需要参数化types ,比如上面的Ordered[A] ,但与String不同。

上下文绑定描述了一个隐式 ,而不是视图边界的隐式转换 。 它用于声明某些types为A的types为B[A]的隐式值。 语法如下所示:

 def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A] 

这比视图更混乱,因为现在还不清楚如何使用它。 在Scala中使用的常见例子是:

 def f[A : ClassManifest](n: Int) = new Array[A](n) 

参数化types的Array初始化需要ClassManifest可用,这是因为与types擦除和数组的非擦除性质有关的神秘原因。

库中另一个非常常见的例子有点复杂:

 def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b) 

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

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

视图界限和上下文界限是如何实现的?

考虑到它们的定义,视图边界和上下文边界都使用隐式参数来实现,这应该不会令人惊讶。 实际上,我展示的语法是真正发生的语法糖。 看下面他们如何去糖:

 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) 

什么是视图边界?

视图边界主要用于利用pimp我的库模式,通过这个模式,在想要以某种方式返回原始types的情况下,可以通过该模式“添加”方法到现有的类。 如果您不需要以任何方式返回该types,那么您不需要视图边界。

视图绑定使用的经典示例是处理Ordered 。 请注意, Int没有Ordered ,例如,虽然存在隐式转换。 之前给出的示例需要一个视图绑定,因为它返回未转换的types:

 def f[A <% Ordered[A]](a: A, b: A): A = if (a < b) a else b 

这个例子不会没有视图范围。 但是,如果我要返回另一个types,那么我不需要一个视图界限:

 def f[A](a: Ordered[A], b: A): Boolean = a < b 

这里的转换(如果需要的话)发生在我将parameter passing给f ,所以f不需要知道它。

除了Ordered之外,库中最常见的用法是处理StringArray类,它们是Java类,就像它们是Scala集合一样。 例如:

 def f[CC <% Traversable[_]](a: CC, b: CC): CC = if (a.size < b.size) a else b 

如果试图做这个没有视图边界,一个String的返回types将是一个WrappedString (斯卡拉2.8),类似的Array

即使types仅用作返回types的types参数,也会发生同样的情况:

 def f[A <% Ordered[A]](xs: A*): Seq[A] = xs.toSeq.sorted 

什么是上下文的界限?

上下文边界主要用于所谓的typestypes ,作为对Haskelltypes类的引用。 基本上,这种模式通过一种隐含的适配器模式来实现可用的function来实现inheritance的替代。

经典的例子是Scala 2.8的Ordering ,它取代了整个Scala库中的Ordered 。 用法是:

 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中的一些隐式转换来启用传统的运算符风格。 Scala 2.8中的另一个例子是Numeric

 def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b) 

一个更复杂的例子是CanBuildFrom的新的集合用法,但是已经有很长的答案了,所以我会在这里避免它。 而且,正如前面提到的那样,还有ClassManifest用法,它需要在没有具体types的情况下初始化新的数组。

与typestypes绑定的上下文更有可能被自己的类使用,因为它们可以分离关注点,而通过良好的devise可以避免在自己的代码中查看边界(它主要用于绕开别人的devise)。

尽pipe已经有很长一段时间了,但是在2010年,上下文边界的使用已经真正开始了,现在Scala最重要的大部分库和框架在某种程度上已经被发现了。 尽pipe如此,使用它的最极端的例子是Scalaz库,它为Haskell带来了很多Scala的力量。 我build议阅读types模式,以更好地了解它可以使用的所有方法。

编辑

感兴趣的相关问题:

  • 关于蕴涵的types,起源和优先性的讨论
  • 链接牵连