简单来说,什么是上下文和视图边界以及它们之间的区别是什么?
一些易于理解的例子也很棒!
我以为这已经被问过了,但是,如果是这样的话,问题在“相关”栏中并不明显 . 所以,这里是:
视图绑定是Scala中引入的一种机制,可以使用某种类型 A ,就好像它是某种类型 B 一样 . 典型的语法是这样的:
A
B
def f[A <% B](a: A) = a.bMethod
换句话说, A 应该有一个隐式转换为 B available,因此可以在 A 类型的对象上调用 B 方法 . 标准库中最常见的视图边界用法(在Scala 2.8.0之前,无论如何),使用 Ordered ,如下所示:
Ordered
def f[A <% Ordered[A]](a: A, b: A) = if (a < b) a else b
因为可以将 A 转换为 Ordered[A] ,并且因为 Ordered[A] 定义了方法 <(other: A): Boolean ,所以我可以使用表达式 a < b .
Ordered[A]
<(other: A): Boolean
a < b
请注意view bounds are deprecated,你应该避免它们 .
上下文边界是在Scala 2.8.0中引入的,通常与所谓的类型类模式一起使用,这种模式是模拟Haskell类型类提供的功能的代码模式,但是更加详细 .
虽然视图边界可以与简单类型一起使用(例如, A <% String ),但是上下文绑定需要参数化类型,例如上面的 Ordered[A] ,但与 String 不同 .
A <% String
String
上下文绑定描述了隐式值,而不是视图绑定的隐式转换 . 它用于声明某些类型 A ,有一个类型为 B[A] 的隐式值 . 语法如下:
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)
参数化类型的 Array 初始化需要 ClassManifest 可用,因为与类型擦除和数组的非擦除性质相关的神秘原因 .
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 .
implicitly
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我的库模式,通过该模式,一个"adds"方法到现有类,在某种情况下你想以某种方式返回原始类型 . 如果您不需要以任何方式返回该类型,则不需要视图绑定 .
视图绑定用法的经典示例是处理 Ordered . 请注意, Int 不是 Ordered ,例如,虽然存在隐式转换 . 之前给出的示例需要一个视图绑定,因为它返回未转换的类型:
Int
def f[A <% Ordered[A]](a: A, b: A): A = if (a < b) a else b
没有视图边界,此示例将不起作用 . 但是,如果我要返回另一个类型,那么我不再需要视图绑定了:
def f[A](a: Ordered[A], b: A): Boolean = a < b
这里的转换(如果需要)在我将参数传递给 f 之前发生,所以 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 的返回类型将是 WrappedString (Scala 2.8),类似于 Array .
WrappedString
即使类型仅用作返回类型的类型参数,也会发生同样的事情:
def f[A <% Ordered[A]](xs: A*): Seq[A] = xs.toSeq.sorted
上下文边界主要用于所谓的类型类模式,作为对Haskell类型类的引用 . 基本上,此模式通过一种隐式适配器模式使功能可用来实现继承的替代方法 .
经典的例子是Scala 2.8的 Ordering ,它在整个Scala的库中取代了 Ordered . 用法是:
Ordering
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 :
Numeric
def f[A : Numeric](a: A, b: A) = implicitly[Numeric[A]].plus(a, b)
一个更复杂的例子是 CanBuildFrom 的新集合用法,但是's already a very long answer about that, so I' ll在这里避免使用它 . 并且,如前所述,有 ClassManifest 用法,这是初始化没有具体类型的新数组所必需的 .
CanBuildFrom
与类型模式绑定的上下文更有可能被您自己的类使用,因为它们可以分离关注点,而良好的设计可以在你自己的代码中避免视图边界(它主要用于绕过别人的设计) .
尽管已经有很长一段时间了,但是在2010年,上下文界限的使用已经真正起步,现在在Scala最重要的大多数库和框架中都有一定程度的发现 . 但是,它最常用的例子是Scalaz库,它为Haskell带来了很多Scala的强大功能 . 我建议阅读类型类模式,以便更好地了解它的所有使用方式 .
EDIT
相关问题:
A discussion on types, origin and precedence of implicits
Chaining implicits
1 回答
我以为这已经被问过了,但是,如果是这样的话,问题在“相关”栏中并不明显 . 所以,这里是:
什么是视界?
视图绑定是Scala中引入的一种机制,可以使用某种类型
A
,就好像它是某种类型B
一样 . 典型的语法是这样的:换句话说,
A
应该有一个隐式转换为B
available,因此可以在A
类型的对象上调用B
方法 . 标准库中最常见的视图边界用法(在Scala 2.8.0之前,无论如何),使用Ordered
,如下所示:因为可以将
A
转换为Ordered[A]
,并且因为Ordered[A]
定义了方法<(other: A): Boolean
,所以我可以使用表达式a < b
.请注意view bounds are deprecated,你应该避免它们 .
什么是上下文绑定?
上下文边界是在Scala 2.8.0中引入的,通常与所谓的类型类模式一起使用,这种模式是模拟Haskell类型类提供的功能的代码模式,但是更加详细 .
虽然视图边界可以与简单类型一起使用(例如,
A <% String
),但是上下文绑定需要参数化类型,例如上面的Ordered[A]
,但与String
不同 .上下文绑定描述了隐式值,而不是视图绑定的隐式转换 . 它用于声明某些类型
A
,有一个类型为B[A]
的隐式值 . 语法如下:这比视图绑定更令人困惑,因为它不能立即清楚如何使用它 . Scala中常见的用法示例如下:
参数化类型的
Array
初始化需要ClassManifest
可用,因为与类型擦除和数组的非擦除性质相关的神秘原因 .库中另一个非常常见的例子有点复杂:
这里,
implicitly
用于检索我们想要的隐式值,类型为Ordering[A]
,该类定义方法compare(a: A, b: A): Int
.我们将在下面看到另一种方法 .
如何实现视图边界和上下文界限?
鉴于它们的定义,视图边界和上下文边界都是使用隐式参数实现的,这应该不足为奇 . 实际上,我展示的语法是真正发生的语法糖 . 见下文他们如何脱糖:
所以,当然,人们可以用完整的语法编写它们,这对于上下文边界特别有用:
View Bounds用于什么?
视图边界主要用于利用pimp我的库模式,通过该模式,一个"adds"方法到现有类,在某种情况下你想以某种方式返回原始类型 . 如果您不需要以任何方式返回该类型,则不需要视图绑定 .
视图绑定用法的经典示例是处理
Ordered
. 请注意,Int
不是Ordered
,例如,虽然存在隐式转换 . 之前给出的示例需要一个视图绑定,因为它返回未转换的类型:没有视图边界,此示例将不起作用 . 但是,如果我要返回另一个类型,那么我不再需要视图绑定了:
这里的转换(如果需要)在我将参数传递给
f
之前发生,所以f
不需要知道它 .除了
Ordered
之外,库中最常见的用法是处理String
和Array
,它们是Java类,就像它们是Scala集合一样 . 例如:如果有人试图在没有视图边界的情况下执行此操作,
String
的返回类型将是WrappedString
(Scala 2.8),类似于Array
.即使类型仅用作返回类型的类型参数,也会发生同样的事情:
什么是上下文界限用于?
上下文边界主要用于所谓的类型类模式,作为对Haskell类型类的引用 . 基本上,此模式通过一种隐式适配器模式使功能可用来实现继承的替代方法 .
经典的例子是Scala 2.8的
Ordering
,它在整个Scala的库中取代了Ordered
. 用法是:虽然你通常会看到这样写的:
利用
Ordering
中的一些隐式转换来启用传统的运算符样式 . Scala 2.8中的另一个例子是Numeric
:一个更复杂的例子是
CanBuildFrom
的新集合用法,但是's already a very long answer about that, so I' ll在这里避免使用它 . 并且,如前所述,有ClassManifest
用法,这是初始化没有具体类型的新数组所必需的 .与类型模式绑定的上下文更有可能被您自己的类使用,因为它们可以分离关注点,而良好的设计可以在你自己的代码中避免视图边界(它主要用于绕过别人的设计) .
尽管已经有很长一段时间了,但是在2010年,上下文界限的使用已经真正起步,现在在Scala最重要的大多数库和框架中都有一定程度的发现 . 但是,它最常用的例子是Scalaz库,它为Haskell带来了很多Scala的强大功能 . 我建议阅读类型类模式,以便更好地了解它的所有使用方式 .
EDIT
相关问题:
A discussion on types, origin and precedence of implicits
Chaining implicits