问题
I have an example like this:
abstract class IsBaseTC[A] { type Self }
abstract class JustHoldsTypeMember[A] extends IsBaseTC[A]
implicit val doubleHoldsTypeMember = new JustHoldsTypeMember[Double] { type Self = Double }
abstract class IsActualTC[A, T](implicit val aIsBaseTc: IsBaseTC[T]) extends IsBaseTC {
type Self = A
def get(self: A): aIsBaseTc.Self
}
case class Container[T](
get: T
)
implicit val containerOfDoubleIsActual = new IsActualTC[Container[Double], Double] {
def get(self: Self) = self.get // type mismatch;
// found self.get.type (with underlying type Double)
// required this.aIsBaseTc.self
}
Which gives me the error shown above. Unless I have failed to follow my own logic through correctly, this.aIsBaseTc.self
should resolve to a Double
. Is there a way to persuade the compiler that this is the case?
Grateful for any help.
回答1:
The thing is in scopes.
Simpler example is
trait A { type T }
implicit val a: A { type T = Int } = null
def test(implicit x: A): Unit = {
implicitly[x.T =:= Int] // doesn't compile, cannot prove that x.T =:= Int
}
You assume that x
is a
(aIsBaseTc
is doubleHoldsTypeMember
in your notations). But actually x
is not a
, x
will be resolved when test
is called (in the scope of test
call site) but a
is defined in current scope (scope of test
definition). Similarly, aIsBaseTc
is not doubleHoldsTypeMember
.
When doing implicit resolution with type parameters, why does val placement matter? (See the difference between implicit x: X
and implicitly[X]
.)
As for any anonymous class
implicit val containerOfDoubleIsActual = new IsActualTC[Container[Double], Double] {
def get(self: Self) = self.get // type mismatch;
}
is a shorthand for
class IsActualTCImpl extends IsActualTC[Container[Double], Double] {
def get(self: Self) = self.get // type mismatch;
// aIsBaseTc is not doubleHoldsTypeMember here
}
implicit val containerOfDoubleIsActual =
new IsActualTCImpl // implicit is resolved here
// aIsBaseTc becomes doubleHoldsTypeMember here
And since aIsBaseTc
is not doubleHoldsTypeMember
, aIsBaseTc.Self
is not Double
.
Possible fix is to add one more type parameter S
to IsActualTC
abstract class IsActualTC[A, T, S](implicit val aIsBaseTc: IsBaseTC[T] {type Self = S}) extends IsBaseTC {
type Self = A
def get(self: A): S
}
implicit val containerOfDoubleIsActual = new IsActualTC[Container[Double], Double, Double] {
def get(self: Self) = self.get
}
or to add a type refinement to the implicit parameter of IsActualTC
abstract class IsActualTC[A, T](implicit val aIsBaseTc: IsBaseTC[T] {type Self = T}) extends IsBaseTC {
type Self = A
def get(self: A): aIsBaseTc.Self
}
implicit val containerOfDoubleIsActual = new IsActualTC[Container[Double], Double] {
def get(self: Self) = self.get
}
回答2:
It is true that both are doubles, but that is the value. It is not the same type.
The type that is requested by def get(self: A)
is aIsBaseTc.Self
, which is in your case doubleHoldsTypeMember.Self
when trying to access self of doubleHoldsTypeMember
you get:
The issue is, that you cannot create this.aIsBaseTc.Self
because it is not accessible.
In order to resolve it, you can try to create an instance of Self
where you can access it:
abstract class IsBaseTC[A] { type Self; def createSelf(): Self }
abstract class JustHoldsTypeMember[A] extends IsBaseTC[A]
implicit val doubleHoldsTypeMember: JustHoldsTypeMember[Double] = new JustHoldsTypeMember[Double] {
type Self = Double
override def createSelf() = 3.14
}
and use it:
implicit val containerOfDoubleIsActual: IsActualTC[Container[Double], Double] = new IsActualTC[Container[Double], Double] {
override def get(self: Self) = this.aIsBaseTc.createSelf()
override def createSelf() = ???
}
Then, when running:
containerOfDoubleIsActual.get(Container(4.12))
it will print:
3.14
which was created at createSelf of doubleHoldsTypeMember
来源:https://stackoverflow.com/questions/64238219/setting-abstract-type-based-on-typeclass