集合

时间:2015-08-17 12:06:38

标签: scala generics polymorphism implicit

我有简化的情况:

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并递归其类型的唯一解决方案,要求所有实例都在上下文中?

2 个答案:

答案 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的参数或得到的结果是正确的。如果是,请发布您需要的签名。