【问题标题】:What is a "context bound" in Scala?Scala 中的“上下文绑定”是什么?
【发布时间】:2011-02-28 06:20:15
【问题描述】:

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

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

【问题讨论】:

标签: scala scala-2.8 context-bound


【解决方案1】:

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

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

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

表示参数可以看作Ordered。比较

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

表示参数有一个关联的Ordering。

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

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

【讨论】:

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

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

【讨论】:

    【解决方案3】:

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

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

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

    上下文绑定必须与* =&gt; * 类型的构造函数一起使用。但是类型构造函数Function1 是一种(*, *) =&gt; *。类型别名的使用部分地应用了类型为 String 的第二个类型参数,从而产生了一个正确类型的类型构造函数以用作上下文绑定。

    有一个提议允许您在 Scala 中直接表达部分应用的类型,而无需在 trait 中使用类型别名。然后你可以写:

    def f3[T : [X](X => String)](t: T) = 0 
    

    【讨论】:

    • 你能解释一下f2定义中#From的含义吗?我不确定 F 类型是在哪里构造的(我说得对吗?)
    • 这称为类型投影,引用To[String] 类型的类型成员From。我们没有为From 提供类型参数,所以我们指的是类型构造函数,而不是类型。这种类型的构造函数是用作上下文绑定的正确类型——* -&gt; *。这通过要求To[String]#From[T] 类型的隐式参数来限制类型参数T。展开类型别名,瞧,剩下的是Function1[String, T]。
    • 应该是 Function1[T, String] 吗?
    【解决方案4】:

    这是另一个括号。

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

    【讨论】:

      猜你喜欢
      • 2021-09-19
      • 2014-04-09
      • 1970-01-01
      • 1970-01-01
      • 2016-09-12
      • 1970-01-01
      • 1970-01-01
      • 2021-10-16
      • 2016-02-02
      相关资源
      最近更新 更多