如何在 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
。但是,下面给出了编译器错误,因为我们不能保证所有元素都匹配 Helper
s:
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 <: Base
或 T >: 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)(_)))
我有一个隐式助手设置如下:
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
。但是,下面给出了编译器错误,因为我们不能保证所有元素都匹配 Helper
s:
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 <: Base
或 T >: 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)(_)))