问题描述
接着从所见之处抽象类型实现类型类我试图在下面的代码片段中并行比较这两种方法:
Following on from Witness that an abstract type implements a typeclassI've tried to compare these two approaches side-by-side in the code snippet below:
// We want both ParamaterizedTC and WithAbstractTC (below) to check that
// their B parameter implements AddQuotes
abstract class AddQuotes[A] {
def inQuotes(self: A): String = s"${self.toString}"
}
implicit val intAddQuotes = new AddQuotes[Int] {}
abstract class ParamaterizedTC[A, _B](implicit ev: AddQuotes[_B]) {
type B = _B
def getB(self: A): B
def add1ToB(self: A): String = ev.inQuotes(getB(self)) // TC witness does not need to be at method level
}
abstract class WithAbstractTC[A] private {
// at this point the compiler has not established that type B implements AddQuotes, even if we have created
// this instance via the apply[A, _B] constructor below...
type B
def getB(self: A): B
def add1ToB(self: A)(implicit ev: AddQuotes[B]): String =
ev.inQuotes(getB(self)) // ... so here the typeclass witness has to occur on the method level
}
object WithAbstractTC {
// This constructor checks that B implements AddQuotes
def apply[A, _B: AddQuotes](getB: A => _B): WithAbstractTC[A] = new WithAbstractTC[A] {
type B = _B
def getB(self: A): B = getB(self)
}
// But we could also have a constructor that does not check, so the compiler can never be certain that
// for a given instance of WithAbstractTC, type B implements AddQuotes
def otherConstructor[A, _B](getB: A => _B): WithAbstractTC[A] { type B = _B } = new WithAbstractTC[A] {
type B = _B
def getB(self: A): B = getB(self)
}
}
case class Container[B: AddQuotes]( get: B )
// These are both fine
implicit def containerIsParamaterized[B: AddQuotes]: ParamaterizedTC[Container[B], B] =
new ParamaterizedTC[Container[B], B] { def getB(self: Container[B]): B = self.get }
implicit def containerIsWithAbstract[_B: AddQuotes]: WithAbstractTC[Container[_B]] =
WithAbstractTC[Container[_B], _B](self => self.get)
val contIsParamaterized: ParamaterizedTC[Container[Int], Int] =
implicitly[ParamaterizedTC[Container[Int], Int]]
val contIsWithAbstract: WithAbstractTC[Container[Int]] =
implicitly[WithAbstractTC[Container[Int]]]
implicitly[AddQuotes[contIsParamaterized.B]]
implicitly[AddQuotes[contIsWithAbstract.B]] // This is not fine
我的结论(如果我错了,请纠正我)是,如果在公共构造函数中存在类型类见证人(如下面的ParamaterizedTC
所示),则编译器始终可以确定B
实现了AddQuotes
.而如果将此见证人放在类型类同伴对象的构造函数中(如WithAbstractTC
),则它不能.与基于抽象类型的方法相比,这在某种程度上改变了基于类型参数的方法的使用.
My conclusion (please correct me if I'm wrong) is that if the typeclass witness exists in the public constructor (as in ParamaterizedTC
below) then the compiler can always be certain that B
implements AddQuotes
. Whereas if this witness is put in a constructor in the typeclass companion object (like for WithAbstractTC
) then it cannot. This somewhat changes the usage of a type-parameter-based approach versus the abstract-type-based approach.
推荐答案
implicitly[AddQuotes[contIsWithAbstract.B]]
拒绝与单/多个构造函数/apply
方法或类型参数/类型成员差异无关.你只是失去类型的改进随处可见.编译器无法检查您是否丢失了类型优化.您有权放弃某个类型的优化而放弃该类型.
implicitly[AddQuotes[contIsWithAbstract.B]]
refusing to compile is not connected with single/multiple constructors/apply
methods or type parameter/type member difference. You just lost type refinements everywhere. Compiler can't check that you lost type refinements. You have the right to upcast a type discarding its refinement.
如果您恢复类型细化,则代码会编译
If you restore type refinements the code compiles
object WithAbstractTC {
def apply[A, _B: AddQuotes](getB: A => _B): WithAbstractTC[A] {type B = _B} =
// ^^^^^^^^^^^^^
new WithAbstractTC[A] {
type B = _B
def getB(self: A): B = getB(self)
}
...
}
implicit def containerIsWithAbstract[_B: AddQuotes]:
WithAbstractTC[Container[_B]] { type B = _B } =
// ^^^^^^^^^^^^^^^
WithAbstractTC[Container[_B], _B](self => self.get)
val contIsWithAbstract: WithAbstractTC[Container[Int]] { type B = Int } =
// ^^^^^^^^^^^^^^^^
shapeless.the[WithAbstractTC[Container[Int]]]
//^^^^^^^^^^^^^
implicitly[AddQuotes[contIsWithAbstract.B]] // compiles
请注意,implicitly
放宽了类型细化,shapeless.the
是安全版本.
Please notice that implicitly
looses type refinements, shapeless.the
is safe version.
implicitly
不够具体 https://typelevel.org/blog/2014/01/18/implicitly_existential.html
如何通过抽象隐式对类成员类型类使用类级隐式约束,请参见 @AlexeyRomanov 的答案.
How to use class-level implicit constraint for type-member type class via abstract implicit see @AlexeyRomanov's answer.
这篇关于使用类型参数与抽象类型实现类型类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!