问题
I believe my understanding on this is correct but I'd like to check. When creating typeclasses, it feels neater to have them take a single type parameter, like TypeClass[A]
. If the typeclass needs to be parameterized in other ways, abstract types can be used, and there is a comparison of the two approaches here:
Abstract types versus type parameters
So far as I have been able to figure out, one thing which is not mentioned in the link is that if using a type parameter, you can witness that the parameter implements a (different) typeclass, likeso:
trait IsValidForTC[A]
abstract class TCWithTypeParam[A, B] (implicit ev: IsValidForTC[B]) {}
If I use an abstract type, I cannot be sure that it implements IsValidForTC
:
abstract class TCWithAbstractType[A] (implicit ev: IsValidForTC[B]) {
type B
} //not found: Type B
If so then this makes sense, but this difference isn't mentioned in the link above so I'd like to check.
Thanks!
回答1:
It's your choice whether to put implicit constraints on class level or method level. This makes impact on when the implicits are resolved.
In a type-parameter type class with implicit parameter you don't constrain the type of type class (applied to type parameters), i.e. type TCWithTypeParam[A, B]
can be used even if there is no implicit IsValidForTC[B]
in a scope. What you do constrain is the constructor of type class. You can emulate this behavior for type-member type class in the following way. Make the constructor private and define apply
method (or instance
as it's called sometimes) in companion object with desired implicit constraint
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 }
}
回答2:
You can add the witness, but it needs to be inside the class scope so it has access to B
:
abstract class TCWithAbstractType[A] {
type B
implicit val ev: IsValidForTC[B]
}
But in practice this is often less convenient than the type parameter, because it has to be implemented explicitly, something like
new TCWithAbstractType[A] {
type B = ...
implicit val ev: IsValidForTC[B] = ...
}
while a constructor parameter just gets the implicit value from outer scope.
Note: this is a partial duplicate of my answer to your follow-up question, but left here in case someone stumbles on this question first.
来源:https://stackoverflow.com/questions/64399785/witness-that-an-abstract-type-implements-a-typeclass