我有简化的情况:
abstract sealed trait Top
class A[T] extends Top
class B[T] extends Top
class Typeclass[T]
implicit def a[T] = new Typeclass[A[T]]
implicit def b[T] = new Typeclass[B[T]]
现在我有一个Map[String, Top]
,并希望对地图中需要在上下文中提供Typeclass
实例的所有值使用操作。这将无法编译,因为地图中的值的具体类型从其类型中不可见,因此我无法为它们设置上下文绑定。
有没有办法告诉编译器实际上总有一个实例可用?在这个例子中,这是给出的,因为有隐式函数为Top
的每个具体子类型生成这些实例。
或者是使用HList
并递归其类型的唯一解决方案,要求所有实例都在上下文中?
答案 0 :(得分:3)
我建议在这种情况下对Oleg Existentials as universals的这种改编使用一些变体...将类型类实例与它的实例值一起打包,
abstract sealed trait Top
class A[T] extends Top
class B[T] extends Top
class Typeclass[T]
implicit def a[T] = new Typeclass[A[T]]
implicit def b[T] = new Typeclass[B[T]]
trait Pack {
type T <: Top
val top: T
implicit val tc: Typeclass[T]
}
object Pack {
def apply[T0 <: Top](t0: T0)(implicit tc0: Typeclass[T0]): Pack =
new Pack { type T = T0 ; val top = t0 ; val tc = tc0 }
}
val m = Map("a" -> Pack(new A[Int]), "b" -> Pack(new B[Double]))
def foo[T: Typeclass](t: T): Unit = ()
def bar(m: Map[String, Pack], k: String): Unit =
m.get(k).map { pack =>
import pack._ // imports T, top and implicit tc
foo(top) // instance available for call of foo
}
bar(m, "a")
答案 1 :(得分:1)
正如在评论中所讨论的那样,在Top
上定义类型类更方便,并且可以通过模式匹配来完成。
假设类型类的部分定义是
def f[T](t: T): FResult[T],
你有相应的实施
def fOnA[T](t: A[T]): FResult[A[T]] = ...
def fOnB[T](t: B[T]): FResult[B[T]] = ...
然后你可以定义
def fOnT(t: Top) : FResult[Top] = t match {
case a: A[_] => fOnA(a)
// provided an FResult[A[T]] is an FResult[Top],
// or some conversion is possible
case b: B[_] => fOnB(b)
}
如果调用通用方法是合法且安全的,例如fOnA[T]
具有存在性(a
匹配A[_]
)
但是,考虑到存在性信息的减少,可能很难说服编译器传递给f
的参数或得到的结果是正确的。如果是,请发布您需要的签名。