如何在 Scala 中强制使用通配符绑定上下文?

How to enforce a context bound on a wildcard in Scala?

我有一个隐式助手设置如下:

trait Helper[T] {
  def help(entry: T): Unit
}

object Helpers {
  implicit object XHelper extends Helper[X] {
    override def help(entry: X): Unit = {println("x")}
  }
  implicit object YHelper extends Helper[Y] {
    override def help(entry: Y): Unit = {println("y")}
  }

  def help[T](entry: T)(implicit helper: Helper[T]): Unit = {
    helper.help(entry)
  }
}

我想建立一个元素集合,并在每个元素上 运行 help。但是,下面给出了编译器错误,因为我们不能保证所有元素都匹配 Helpers:

val data = Seq[_](new X(), new Y())
data.foreach(entry => Helpers.help(entry))

如果我们有一个泛型类型 T,我们可以使用 [T: Helper] 对其实施隐式约束,但这对 _ 不起作用。我如何强制 data 的每个元素都有匹配的 Helper

Seq 这样的类型是不可能的,因为它只针对一种元素类型进行参数化,而这种元素类型对于所有元素都是通用的。

但是,您可以使用 Shapeless HList 和多态函数 (Poly) 实现此目的:

class X
class Y

trait Helper[T] {
  def help(entry: T): Unit
}

object Helpers {
  implicit object XHelper extends Helper[X] {
    override def help(entry: X): Unit = println("x")
  }

  implicit object YHelper extends Helper[Y] {
    override def help(entry: Y): Unit = println("y")
  }
}

import shapeless._

object helper extends Poly1 {
  implicit def tCase[T: Helper]: Case.Aux[T, Unit] = 
    at(implicitly[Helper[T]].help(_))
}

val hlist = new X :: new Y :: HNil
hlist.map(helper)

// Output:
x
y

在 Scala 上下文绑定中,像 class A[T: Typeclass] 只是 class A[T](implicit ev: Typeclass[T]) 的语法糖。与 T <: BaseT >: Super 不同,上下文绑定实际上并不是类型签名的一部分,因此您不能拥有像 val b: Box[T: Typeclass] 这样的签名。

如果你想 运行 对某些容器的元素进行类型类操作,你必须将相关的类型类实例与容器中的值一起打包。

一个可能的实现如下所示:

import language.higherKinds
import language.implicitConversions

// Class that packs values with typeclass instances
class WithTC[T, TC[_]](t: T)(implicit tc: TC[T]) {
  // Some helper methods to simplify executing typeclass operations
  // You may just make `t` and `tc` public, if you wish.
  def apply[U](op: (TC[T], T) => U) = op(tc, t)
  def apply[U](op: T => TC[T] => U) = op(t)(tc)
}
object WithTC {
  // Implicit conversion to automatically wrap values into `WithTC`
  implicit def apply[T, TC[_]](t: T)(implicit tc: TC[T]): WithTC[T, TC] =
    new WithTC(t)(tc)
}

然后你可以用存在类型的元素创建一个序列:

import Helpers._
val data: Seq[(T WithTC Helper) forSome { type T }] = Seq(new X(), new Y())

并对序列元素执行类型类操作:

// The following lines produce equivalent results
data.foreach(_(_ help _))
data.foreach(_(t => implicit tc => Helpers.help(t)))
data.foreach(_(t => Helpers.help(t)(_)))