见证一个抽象类型实现了一个类型类

Witness that an abstract type implements a typeclass

我相信我对此的理解是正确的,但我想核实一下。创建类型类时,让它们采用单个类型参数感觉更整洁,例如 TypeClass[A]。如果类型类需要通过其他方式参数化,可以使用抽象类型,这里有两种方式的比较: Abstract types versus type parameters

据我所知,link 中没有提到的一件事是,如果使用类型参数,您可以看到该参数实现了一个(不同的)类型类,喜欢:

trait IsValidForTC[A]
    
abstract class TCWithTypeParam[A, B] (implicit ev: IsValidForTC[B]) {} 

如果我使用抽象类型,我无法确定它是否实现了 IsValidForTC:

abstract class TCWithAbstractType[A] (implicit ev: IsValidForTC[B]) {
    type B
} //not found: Type B

如果是这样,那么这是有道理的,但是上面的 link 中没有提到这种差异,所以我想检查一下。

谢谢!

您可以选择是在 class 级别还是方法级别设置隐式约束。这会对解析隐含项的时间产生影响。

在带有隐式参数的 type-parameter 类型 class 中,您不限制类型 class 的类型(应用于类型参数),即类型 TCWithTypeParam[A, B] 可以即使范围内没有隐式 IsValidForTC[B] 也可以使用。你做的约束是class类型的构造函数。您可以通过以下方式为 type-member type class 模拟此行为。将构造函数设为私有并在具有所需隐式约束的伴随对象中定义 apply 方法(或有时称为 instance

abstract class TCWithAbstractType[A] private {
  type B
}

object TCWithAbstractType {
  def apply[A, _B: IsValidForTC]: TCWithAbstractType[A] { type B = _B } = 
    new TCWithAbstractType[A] { type B = _B }
}

您可以添加见证,但它需要在 class 范围内,以便它可以访问 B:

abstract class TCWithAbstractType[A] {
    type B
    implicit val ev: IsValidForTC[B]
}

但实际上这通常不如类型参数方便,因为它必须显式实现,比如

new TCWithAbstractType[A] {
    type B = ...
    implicit val ev: IsValidForTC[B] = ...
}

而构造函数参数只是从外部范围获取隐式值。

注意:这是我对 的回答的部分重复,但留在这里以防有人首先偶然发现这个问题。