【问题标题】:Context bounds for generic polymorphic data in collection集合中通用多态数据的上下文边界
【发布时间】:2015-08-17 12:06:37
【问题描述】:

我有简化的情况:

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

【问题讨论】:

  • 是否有理由不在 Top 上定义类型类实例(或作为补充?)无论如何,编译器不会因为知道实例可用而感到满意。它会想在编译时知道哪个实例。
  • @DidierDupont Typeclass 实例的实现取决于T 的类型以及AB 的成员(在我的真实世界用例中)。我不知道如何在 Top 上实现它。
  • @DidierDupont 通过模式匹配,我可以区分AB,但据我所知,我无法从中获取类型T(不匹配每种可能的类型@ 987654334@).
  • 好的,去回答以获得更多空间。

标签: scala generics polymorphism implicit


【解决方案1】:

我建议在这种情况下对 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")

【讨论】:

  • 当 foo 签名不那么琐碎时,它会比模式匹配更好吗?我的直觉是它可能(当然,它不会更糟),但我无法确定它会发生的情况。
  • 模式匹配总是容易受到擦除问题的影响。这种方法不是因为实例是在值的静态类型已知的点选择的。
【解决方案2】:

正如评论中所讨论的,在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 的参数或您得到的结果是好的。如果是这样,请张贴您需要的签名。

【讨论】:

  • 如果fOnAfOnB 在它们的类型T 上有(不同的)上下文边界怎么办?
  • 在存在时调用该方法是合法且安全的,但是将结果强制返回到预期的结果类型 fOnT 既不合法也不安全...你需要一个不安全的转换case 子句的主体来编译它。
  • @MilesSabin :一般情况下是这样,但它在许多情况下都会起作用,例如它的返回类型根本不是参数或协变的。参数也可能是个问题。显然,每个 A[T] 和 B[T] 上的幺半群都不会使它成为 Top 上的幺半群,并且强制转换不会使它成为。
猜你喜欢
  • 1970-01-01
  • 2017-03-03
  • 1970-01-01
  • 2014-08-22
  • 2017-02-01
  • 2019-10-03
  • 1970-01-01
  • 1970-01-01
  • 2015-04-23
相关资源
最近更新 更多