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

Aar*_*rup 15 scala typeclass context-bound

注意:我正在提出这个问题来自己回答,但欢迎其他答案.

请考虑以下简单方法:

def add[T](x: T, y: T)(implicit num: Numeric[T]) = num.plus(x,y)
Run Code Online (Sandbox Code Playgroud)

我可以使用如下的上下文绑定重写它

def add[T: Numeric](x: T, y: T) = ??.plus(x,y) 
Run Code Online (Sandbox Code Playgroud)

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

Aar*_*rup 24

使用隐式方法

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

def add[T: Numeric](x: T, y: T) = implicitly[Numeric[T]].plus(x,y)
Run Code Online (Sandbox Code Playgroud)

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

引用证据参数(不要!)

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

def add[T: Numeric](x: T, y: T) = evidence$1.plus(x,y)
Run Code Online (Sandbox Code Playgroud)

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

更高层次的背景(介绍context方法)

相反,人们可以使用该implicitly方法的增强版本.请注意,隐式方法定义为

def implicitly[T](implicit e: T): T = e
Run Code Online (Sandbox Code Playgroud)

此方法只依赖于编译器将来自周围作用域的正确类型的隐式对象插入到方法调用中,然后返回它.我们可以做得更好一点:

def context[C[_], T](implicit e: C[T]) = e
Run Code Online (Sandbox Code Playgroud)

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

def add[T: Numeric](x: T, y: T) = context.plus(x,y)
Run Code Online (Sandbox Code Playgroud)

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

class Context[T] { def apply[C[_]]()(implicit e: C[T]) = e }
def context[T] = new Context[T]
Run Code Online (Sandbox Code Playgroud)

此版本要求我们每次都指定类型参数,但处理多个类型参数.

def add[T: Numeric](x: T, y: T) = context[T]().plus(x,y)
Run Code Online (Sandbox Code Playgroud)

  • 男孩,如果我以为任何人都依赖于证据参数的名称那么我每周更换一次......而且,这种技术在我的司法管辖范围内是不合法的,但也许法律在你住的地方不同. (8认同)
  • 用`context`方法聪明地破解! (5认同)

kas*_*ens 7

至少从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
Run Code Online (Sandbox Code Playgroud)