什么是Scala上下文和视图边界?
简而言之,上下文和观点界限是什么?它们之间有什么区别?
一些容易遵循的例子也会很棒!
我以为这已经被问到了,但是,如果是这样的话,这个问题在“相关”栏中就不明显了。 所以,这里是:
什么是视图绑定?
视图绑定是Scala中引入的一种机制,可以使用某些typesA
,就好像它是某种typesB
。 典型的语法是这样的:
def f[A <% B](a: A) = a.bMethod
换句话说, A
应该有一个隐式转换为可用的B
,以便可以在A
types的对象上调用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
之外,库中最常见的用法是处理String
和Array
类,它们是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,起源和优先性的讨论
- 链接牵连