见证一个抽象类型实现一个类型类。
见证一个抽象类型实现一个类型类。
我相信我对此的理解是正确的,但我想确认一下。创建类型类时,让它们只接受一个类型参数(如`TypeClass[A]`)会更整洁。如果类型类需要以其他方式进行参数化,可以使用抽象类型,这里有两种方法的比较:
据我所了解,在链接中没有提到的一点是,如果使用类型参数,可以证明该参数实现了一个(不同的)类型类,如下所示:
trait IsValidForTC[A] abstract class TCWithTypeParam[A, B] (implicit ev: IsValidForTC[B]) {}
如果使用抽象类型,我无法确保它实现了`IsValidForTC`:
abstract class TCWithAbstractType[A] (implicit ev: IsValidForTC[B]) { type B } //找不到:类型B
如果是这样的话,这是有道理的,但上面的链接中没有提到这个差别,所以我想确认一下。
谢谢!
问题的原因是在使用抽象类型实现类型类时,编译器无法在类级别确定抽象类型是否实现了类型类的约束。这是因为抽象类型可能有其他的构造函数,这些构造函数不一定强制要求抽象类型实现类型类的约束。因此,需要将类型类的证明放在方法级别,这虽然在功能上是可以的,但需要更多的样板代码。
为了解决这个问题,可以将类型类的构造函数设为私有,并在伴生对象中定义一个带有所需隐式约束的apply方法。通过这种方式,可以在方法级别上进行类型类的约束,并且可以保证抽象类型实现了类型类的约束。
下面是一个使用这种解决方法的示例:
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 } }
在这个示例中,TCWithAbstractType类的构造函数被设置为私有,并在伴生对象中定义了一个apply方法。这个apply方法接受类型参数A和一个隐式参数_B,其中_B必须满足IsValidForTC的约束。通过这种方式,可以在使用TCWithAbstractType时保证抽象类型B实现了IsValidForTC的约束。
需要注意的是,私有构造函数仍然可以在伴生对象中访问(而隐式实例通常是在伴生对象中定义的),可以将主构造函数设置为private,将所有的辅助构造函数设置为private[this],这样它们在伴生对象中也将无法访问。
总结起来,通过在伴生对象中定义带有所需隐式约束的apply方法,可以在方法级别上实现抽象类型对类型类的约束。这种解决方法可以兼顾类型类的约束和灵活性。
问题的出现原因是:在实现一个类型类时,如果使用抽象类型(abstract type)作为类型参数,需要确保抽象类型的证明(witness)可以在类的作用域内访问。然而,这种方法通常不如使用类型参数方便,因为抽象类型的证明必须显式地实现。
解决方法是:将抽象类型和证明(witness)放在类的作用域内,并通过构造函数参数传递隐式值。这样可以从外部作用域获取隐式值,避免在每次实现时显式地指定证明。
以下是示例代码:
abstract class TCWithAbstractType[A] { type B implicit val ev: IsValidForTC[B] } new TCWithAbstractType[A] { type B = ... implicit val ev: IsValidForTC[B] = ... }
注意:这个解决方法的部分内容与我在你的后续问题中的答案相似,但仍保留在这里,以防有人先看到这个问题。