简体   繁体   English

Scala 中的上下文绑定

[英]Context bound in Scala

I am learning Context bound in Scala.我正在学习 Scala 中的上下文绑定。

In the below code, I am invoking multiplication operator on the integer parameter.在下面的代码中,我对整数参数调用乘法运算符。 But it errors out.但它会出错。 'a' is considered as type parameter; 'a' 被视为类型参数; but it is actually not as per my understanding.但实际上并不符合我的理解。 Can someone please help.有人可以帮忙吗。

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

Thanks!谢谢!

Context bounds are syntactic sugar for implicit generic parameters that are parameterized by some type you're using.上下文边界是隐式泛型参数的语法糖,这些参数由您正在使用的某种类型参数化。 This concept is also known as "type class".这个概念也被称为“类型类”。 You define some generic trait, such as your Sample[T] , and then you provide implicit(!) instances of that trait for various concrete values of T .您定义了一些通用特征,例如您的Sample[T] ,然后为T各种具体值提供该特征的隐式(!)实例。 We call them "type class instances".我们称它们为“类型类实例”。

Why implicit?为什么是隐式? It's an implementation detail that Scala uses to achieve the type class mechanism;这是Scala用来实现类型类机制的一个实现细节; type classes also exist in eg Haskell, but the mechanism itself is a bit different.类型类也存在于例如 Haskell 中,但机制本身有点不同。 Anyways, you can then define a method such as your def method which requires a type class instance for some type.无论如何,您可以定义一个方法,例如您的def method ,它需要某个类型的类型类实例。 And you can do this with context bound syntax, or with a more verbose and more explicit standard syntax for implicit parameters.并且您可以使用上下文绑定语法或使用更详细和更明确的隐式参数标准语法来执行此操作。

Your definition is using the context bound.您的定义正在使用上下文绑定。 But there is something wrong with your example, as indicated by the compilation error.但是您的示例有问题,如编译错误所示。 Let's first see a proper example that uses the type class concept correctly.让我们首先看一个正确使用类型类概念的正确示例。

// 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 }
}

And now the usage:现在的用法:

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

What we're doing is saying - there is some value t of type T , we don't know much about it, but what we do know is that there is a Sample type class instance available for it.我们正在做的是说 - 有一个T类型的值t ,我们对此知之甚少,但我们所知道的是有一个Sample类型类实例可用于它。 This allows us to do t.getSample .这允许我们做t.getSample

And now, to finally provide the answer to your problem:现在,最终为您的问题提供答案:

In your code, you are mixing things up.在您的代码中,您正在混淆事物。 Your T is actually called Int .你的T实际上被称为Int You intended to use the Int type, but what you did instead is that you named your generic parameter Int .您打算使用Int类型,但您所做的是将泛型参数命名为Int I could have answered this with way less text, but I figured perhaps you would find the bigger picture interesting, rather than just pointing out the mistake.我本可以用更少的文字来回答这个问题,但我想也许你会发现更大的图景很有趣,而不是仅仅指出错误。

The type parameter named Int does not represent concrete integer type scala.Int .名为Int的类型参数不代表具体的整数类型scala.Int Instead it is just a confusing coincidence that the type parameter was given the same name Int as the concrete type.相反,类型参数被赋予与具体类型相同的名称Int只是一个令人困惑的巧合。 If you give it some other name such as T如果你给它一些其他的名字,比如T

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

the error message should make more sense.错误消息应该更有意义。 Now we see * is not defined for T since Sample type class does not yet provide such capability.现在我们看到*没有为T定义,因为Sample类型类还没有提供这样的能力。 Here is an example of how correct syntactic usage might look usage这是正确的语法用法可能看起来如何使用的示例

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)

You could also have a look at Numeric type class which provides such functionality out of the box您还可以查看提供开箱即用功能的Numeric类型类

def method[T](a: T)(implicit num: Numeric[T]): T = num.times(a, a)

Your method has a type parameter called Int which shadows the actual Int , just like defining a normal variable would shadow something from an outer scope.您的方法有一个名为Int的类型参数,它遮蔽了实际的Int ,就像定义一个普通变量会遮蔽外部作用域的某些东西一样。 The same would happen if you remove the context bound.如果您删除上下文绑定,也会发生同样的情况。

What you are probably trying to do is something closer to the following:您可能正在尝试做的事情更接近以下内容:

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

You can play around with this code here on Scastie.您可以在 Scastie 上使用此代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM