【问题标题】:Extending a class with a type constructor使用类型构造函数扩展类
【发布时间】:2021-02-13 09:35:35
【问题描述】:

我正在尝试使用单个 [A] 类型参数扩展基类,并使用具有 A[_] 类型参数的子类 - 像这样:

abstract class IsBase[A]

abstract class IsSub[A[_]] extends IsBase[A[_]] {
  type T
  def get(self: A[T]): T 
}

implicit def listIsSub[_T] = new IsSub[List] {
  type T = _T;
  def get(self: List[T]): T = self(0)
}
val list = List(1, 2)

implicitly[IsSub[List]{ type T = Int }].get(list) // works fine
implicitly[IsBase[List[Int]]] // Could not find implicit value for parameter e

我知道这样做的一种方法是将抽象类型 T 移动到类型参数,就像这样:

abstract class IsSub1[A[_], T] extends IsBase[A[T]]

但在我继续这条路线之前,我只想检查一下是否没有一种直接的方法可以使这项工作保持原样。

谢谢!

【问题讨论】:

  • 我不明白你需要 IsSub 和 IsBase 做什么,但我很确定你必须将 T 设置为 IsSub 的类型参数。当您说extends IsBase[A[_]] 时,A[_] 与A[_] 类型参数不同。您正在使用通配符,这使得它几乎没有用。我建议使用IsSub[A[_], T] extends IsBase[A[T]] 或类似的东西。无论如何,这比使用结构类型要容易得多。
  • 您声明 Base 采用完整类型,同时声明 Sub 采用类型构造函数。它们是不同的签名。你需要保持一致。除了声明第二个参数T,你还可以声明IsBase[C[_]],然后同样声明子IsSub[C[_]] extends IsBase[C]
  • @user - 谢谢 - 我确实在我的问题中提到了该解决方案,但获得一些外部验证仍然很好。我觉得这是更好的长期方式。

标签: scala generics typeclass implicit existential-type


【解决方案1】:

您应该尝试的the first things 之一是手动解决隐式问题,看看会发生什么

implicitly[IsBase[List[Int]]](listIsSub[Int])

//type mismatch;
// found   : App.IsSub[List]{type T = Int}
// required: App.IsBase[List[Int]]
//Note: List[_] >: List[Int] 
// (and App.IsSub[List]{type T = Int} <: App.IsBase[List[_]]), 
//  but class IsBase is invariant in type A.
//You may wish to define A as -A instead. (SLS 4.5)

所以你可以看到你只是没有定义必要的隐式。您定义了 IsSub[List]{type T = Int} 类型的隐式,它没有连接到 IsBase[List[Int]],因为 IsSub(带有类型构造函数类型参数 A[_])扩展了带有存在类型参数的 IsBase[A[_]]。存在(和类型投影)rarely 与隐式配合得很好。

你可以看到提示。你可以使IsBase逆变

abstract class IsBase[-A]

然后

implicitly[IsBase[List[Int]]]

编译。

是否定义你的类型类逆变(1234)取决于你的逻辑。

【讨论】:

  • 还有——那个类型级别的博客系列真的很有帮助,希望我早点遇到它们。
猜你喜欢
  • 2022-01-22
  • 1970-01-01
  • 2013-11-15
  • 1970-01-01
  • 1970-01-01
  • 2014-05-29
  • 2023-03-26
  • 2019-01-07
  • 1970-01-01
相关资源
最近更新 更多