This is hard to phrase, so please, let me show an example:
trait Cache
trait QueryLike {
type Result
}
trait Query[A] extends QueryLike {
type Result = A
def exec: Result
}
trait CachedQuery[A] extends QueryLike {
type Result = A
def execWithCache(cache: Cache): Result
}
def exec(query: QueryLike)(implicit cache: Cache): query.Result = query match {
case q: Query[query.Result] => q.exec
case cq: CachedQuery[query.Result] => cq.execWithCache(cache)
}
This compiles and runs fine as pattern matching is done on different types (Query
, CachedQuery
) instead of relying on generics like this question.
But I still get compiler warning like :
Warning:(18, 12) abstract type Result in type pattern A$A4.this.Query[query.Result] is unchecked since it is eliminated by erasure case q: Query[query.Result] => q.exec
Since I don't work on dependent type query.Result
directly in anyway (like casting it for different operations), it'd be ideal to just erase it completely and do away with the warning. But unfortunately, using wildcard doesn't work for a good reason:
...
case q: Query[_] => q.exec // type mismatch
case cq: CachedQuery[_] => cq.execWithCache(cache)
...
Is there a better way to do this without generating compiler warning?
This error isn't really specific to path-dependent types. If you tried to match on any Query[A]
you would get the same error, because the type parameter A
is erased at runtime. In this case, it's not possible that the type parameter can be anything other than the type you're looking for. Since a Query[A]
is a QueryLike { type Result = A}
, it should also be a Query[query.Result]
, though this is a somewhat unusual way to look at it. You could use the @unchecked
annotation to suppress the warning, if you wish:
def exec(query: QueryLike)(implicit cache: Cache): query.Result = query match {
case q: Query[query.Result @unchecked] => q.exec
case cq: CachedQuery[query.Result @unchecked] => cq.execWithCache(cache)
}
While it's tough to say if this would apply to your actual use-case, you could also restructure your code to avoid matching entirely, and handle it (possibly) more elegantly via polymorphism. Since the last exec
requires an implicit Cache
anyway, it wouldn't seem to hurt to allow that for each QueryLike
. Your API can be more uniform this way, and you wouldn't need to figure out which method to call.
trait Cache
trait QueryLike {
type Result
def exec(implicit cache: Cache): Result
}
trait Query[A] extends QueryLike {
type Result = A
}
trait CachedQuery[A] extends QueryLike {
type Result = A
}
def exec(query: QueryLike)(implicit cache: Cache): query.Result = query.exec
If Query[A]
requires an exec
without a Cache
, you could also provide an overload with a DummyImplicit
to allow it to work without one.
def exec(implicit d: DummyImplicit): Result