1

我正在学习 Scala 中的上下文绑定。

在下面的代码中,我在整数参数上调用乘法运算符。但它出错了。'a' 被视为类型参数;但实际上并不符合我的理解。有人可以帮忙吗。

scala> class Sample[T]
defined class Sample

scala> def method[Int:Sample](a:Int) = a * a
<console>:12: error: value * is not a member of type parameter Int
       def method[Int:Sample](a:Int) = a * a

谢谢!

4

3 回答 3

5

上下文边界是隐式泛型参数的语法糖,这些参数由您正在使用的某种类型参数化。这个概念也被称为“类型类”。您定义一些通用特征,例如您的Sample[T],然后为 的各种具体值提供该特征的隐式(!)实例T。我们称它们为“类型类实例”。

为什么是隐含的?这是Scala用来实现类型类机制的一个实现细节;类型类也存在于例如 Haskell 中,但机制本身有点不同。无论如何,您可以定义一个方法,例如您的方法,该方法def method需要某种类型的类型类实例。您可以使用上下文绑定语法,或者使用更详细和更明确的隐式参数标准语法来做到这一点。

您的定义是使用上下文绑定。但是如编译错误所示,您的示例有问题。让我们首先看一个正确使用类型类概念的正确示例。

// type class definition:

trait Sample[T] {
  def getSample: T
}

// type class instance(s):

object Sample {
  implicit val sampleInt: Sample[Int] = 
    new Sample[Int] { def getSample = 42 }
}

现在的用法:

import Sample._

// using the context bound syntax
def method1[T : Sample](t: T) = t.getSample

// not using the context bound syntax
def method2(t: T)(implicit ev: Sample[T]) = t.getSample

我们所做的是说 - ttype有一些值T,我们对此知之甚少,但我们所知道的是有一个Sample可用的类型类实例。这让我们可以做到t.getSample

现在,最终为您的问题提供答案:

在你的代码中,你把事情搞混了。你T实际上被称为Int. 您打算使用该Int类型,但您所做的是您将泛型参数命名为Int。我本可以用更少的文字来回答这个问题,但我想也许你会发现更大的图景很有趣,而不仅仅是指出错误。

于 2021-07-09T10:45:03.660 回答
4

命名的类型参数Int不代表具体的整数类型scala.IntInt相反,类型参数被赋予与具体类型相同的名称只是一个令人困惑的巧合。如果你给它一些其他的名字,比如T

def method[T: Sample](a: T): T = a * a

错误消息应该更有意义。现在我们看到*is not defined forT因为Sample类型类还没有提供这样的能力。这是一个正确的语法用法可能看起来如何的示例

trait Sample[T] {
  def mult(a: T, b: T): T
}

def method[T: Sample](a: T): T = implicitly[Sample[T]].mult(a, a)
def method[T](a: T)(implicit ev: Sample[T]): T = ev.mult(a, a)

您还可以查看Numeric提供开箱即用此类功能的类型类

def method[T](a: T)(implicit num: Numeric[T]): T = num.times(a, a)
于 2021-07-09T10:43:28.267 回答
2

您的方法有一个名为的类型参数Int,它会遮蔽实际Int,就像定义普通变量会遮蔽外部范围的某些内容一样。如果您删除上下文绑定,也会发生同样的情况。

您可能正在尝试做的是更接近以下内容:

trait Sample[A] {
  def someOperation(a1: A, a2: A): A
}

implicit object IntSample extends Sample[Int] {
  override def someOperation(a1: Int, a2: Int): Int = a1 * a2
}

def method[T: Sample](t: T) = implicitly[Sample[T]].someOperation(t, t)

method(4) // compiles and returns 16
//method("4") // doesn't compile, no implicit instance of Sample[String] in scope

您可以在 Scastie 上使用此代码。

于 2021-07-09T10:46:18.217 回答