如何获取与上下文绑定关联的类型类的实例?

发布于 2024-10-07 00:35:47 字数 417 浏览 1 评论 0原文

注意:我提出这个问题是为了自己回答,但欢迎其他答案。

考虑以下简单方法:

def add[T](x: T, y: T)(implicit num: Numeric[T]) = num.plus(x,y)

我可以使用 上下文绑定如下,

def add[T: Numeric](x: T, y: T) = ??.plus(x,y) 

但如何获取 Numeric[T] 类型的实例我可以调用 plus 方法吗?

Note: I'm posing this question to answer it myself, but other answers are welcome.

Consider the following simple method:

def add[T](x: T, y: T)(implicit num: Numeric[T]) = num.plus(x,y)

I can rewrite this using a context bound as follows

def add[T: Numeric](x: T, y: T) = ??.plus(x,y) 

but how do I get an instance of the Numeric[T] type so that I can invoke the plus method?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

苏别ゝ 2024-10-14 00:35:47

使用隐式方法

最常见和通用的方法是使用隐式方法,在Predef中定义:

def add[T: Numeric](x: T, y: T) = implicitly[Numeric[T]].plus(x,y)

显然,这有点冗长,需要重复类型类的名称。

引用证据参数不要!

另一种选择是使用编译器自动生成的隐式证据参数的名称:

def add[T: Numeric](x: T, y: T) = evidence$1.plus(x,y)

令人惊讶的是,这种技术甚至是合法的,并且在实践中不应该依赖它,因为证据参数的名称可能会改变。

更高类型的上下文引入上下文方法

相反,我们可以使用隐式的增强版本代码>方法。请注意,隐式方法被定义为:

def implicitly[T](implicit e: T): T = e

该方法仅依赖于编译器将正确类型的隐式对象从周围范围插入到方法调用中,然后返回它。我们可以做得更好一点:

def context[C[_], T](implicit e: C[T]) = e

这允许我们将 add 方法定义为

def add[T: Numeric](x: T, y: T) = context.plus(x,y)

context 方法类型参数 NumericT 是从范围推断出来的!不幸的是,在某些情况下这个context方法不起作用。例如,当类型参数具有多个上下文边界或存在具有不同上下文边界的多个参数时。我们可以用稍微复杂的版本来解决后一个问题:

class Context[T] { def apply[C[_]]()(implicit e: C[T]) = e }
def context[T] = new Context[T]

这个版本要求我们每次都指定类型参数,但可以处理多个类型参数。

def add[T: Numeric](x: T, y: T) = context[T]().plus(x,y)

Using the implicitly method

The most common and general approach is to use the implicitly method, defined in Predef:

def add[T: Numeric](x: T, y: T) = implicitly[Numeric[T]].plus(x,y)

Obviously, this is somewhat verbose and requires repeating the name of the type class.

Referencing the evidence parameter (don't!)

Another alternative is to use the name of the implicit evidence parameter automatically generated by the compiler:

def add[T: Numeric](x: T, y: T) = evidence$1.plus(x,y)

It's surprising that this technique is even legal, and it should not be relied upon in practice since the name of the evidence parameter could change.

Context of a Higher Kind (introducing the context method)

Instead, one can use a beefed-up version of the implicitly method. Note that the implicitly method is defined as

def implicitly[T](implicit e: T): T = e

This method simply relies on the compiler to insert an implicit object of the correct type from the surrounding scope into the method call, and then returns it. We can do a bit better:

def context[C[_], T](implicit e: C[T]) = e

This allows us to define our add method as

def add[T: Numeric](x: T, y: T) = context.plus(x,y)

The context method type parameters Numeric and T are inferred from the scope! Unfortunately, there are circumstances in which this context method will not work. When a type parameter has multiple context bounds or there are multiple parameters with different context bounds, for example. We can resolve the latter problem with a slightly more complex version:

class Context[T] { def apply[C[_]]()(implicit e: C[T]) = e }
def context[T] = new Context[T]

This version requires us to specify the type parameter every time, but handles multiple type parameters.

def add[T: Numeric](x: T, y: T) = context[T]().plus(x,y)
∞梦里开花 2024-10-14 00:35:47

至少从 Scala 2.9 开始你可以执行以下操作:

import Numeric.Implicits._
def add[T: Numeric](x: T, y: T) = x + y

add(2.8, 0.1) // res1: Double = 2.9
add(1, 2) // res2: Int = 3

At least since Scala 2.9 you can do the following:

import Numeric.Implicits._
def add[T: Numeric](x: T, y: T) = x + y

add(2.8, 0.1) // res1: Double = 2.9
add(1, 2) // res2: Int = 3
望笑 2024-10-14 00:35:47

这个答案描述了另一种方法,该方法可以产生更具可读性、自文档化的客户端代码。

动机

我之前描述的context方法是一个非常通用的解决方案,适用于任何类型类,无需任何额外的工作。但是,由于以下两个原因,这可能是不可取的:

  • 当类型参数具有多个上下文边界时,不能使用 context 方法,因为编译器无法确定要使用哪个上下文边界。< /p>

  • 对通用context方法的引用损害了客户端代码的可读性。

特定于类型类的方法

使用与所需类型类相关的方法可以使客户端代码更具可读性。这是标准库中用于 Manifest 类型类的方法:

// definition in Predef
def manifest[T](implicit m: Manifest[T]) = m

// example usage
def getErasure[T: Manifest](x: T) = manifest[T].erasure

概括此方法

使用特定于类型类的方法的主要缺点是必须为每个类型类定义一个附加方法。我们可以通过以下定义简化此过程:

class Implicitly[TC[_]] { def apply[T]()(implicit e: TC[T]) = e }
object Implicitly { def apply[TC[_]] = new Implicitly[TC] }

然后可以为任何类型类定义新的特定于类型类的隐式样式方法:

def numeric = Implicitly[Numeric]
// or
val numeric = Implicitly[Numeric]

最后,客户端代码可以按如下方式使用隐式:

def add[T: Numeric](x: T, y: T) = numeric[T].plus(x, y)

This answer describes another approach that results in more-readable, self-documenting client code.

Motivation

The context method that I described previously is a very general solution that works with any type class, without any additional effort. However, it may be undesirable for two reasons:

  • The context method cannot be used when the type parameter has multiple context bounds, since the compiler has no way to determine which context bound is intended.

  • The reference to the generic context method harms readability of the client code.

Type-class-specific methods

Using a method that is tied to the desired type class makes client code much more readable. This is the approach used in the standard library for the Manifest type class:

// definition in Predef
def manifest[T](implicit m: Manifest[T]) = m

// example usage
def getErasure[T: Manifest](x: T) = manifest[T].erasure

Generalizing this approach

The main drawback of using type-class-specific methods is that an additional method must be defined for every type class. We can ease this process with the following definitions:

class Implicitly[TC[_]] { def apply[T]()(implicit e: TC[T]) = e }
object Implicitly { def apply[TC[_]] = new Implicitly[TC] }

Then a new type-class-specific implicitly-style method can be defined, for any type class:

def numeric = Implicitly[Numeric]
// or
val numeric = Implicitly[Numeric]

Finally, client code can use the Implicitly as follows:

def add[T: Numeric](x: T, y: T) = numeric[T].plus(x, y)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文