I have this code:
class A extends Testable { type Self <: A }
class B extends A { type Self <: B }
trait Testable {
type Self
def test[T <: Self] = {}
}
object Main {
val h = new A
// this throws an error
h.test[B]
}
And my error is:
error: type arguments [B] do not conform to method test's type parameter bounds [T <: Main.h.Self]
h.test[B]
On this question, it was said that this was due to path dependent types. Can anyone figure out how to have T <: Self, without having the path-dependent types problem?
Any help would be appreciated.
I think what you are trying to achieve is something like this:
But this doesn't make sense. Why? Because you can very easily circumvent such constraint, effectively rendering it pointless:
but...
Not only the constraint provides zero additional type safety, I think it also leads to breakage one of the most fundamental rules of OOP - the Liskov Substitution Principle. The snippet above compiles when
h
is of typeA
but does not compile whenh
is of typeB
even though it's a subtype ofA
, so everything should be fine according to the LSP.So, essentially if you just leave your types like this:
you have exactly the same level of type safety.
Your code need to be looks like:
But it is imposible in current version of scala.
I would propose little bit long way (not longer than using path dependent types but another), and it conforms your requirements.
a) Use Type-class pattern for test method;
b) Use implicit parameters for conforms type relations.
Class hierarchy:
Conforms trait:
Testable Type-class:
test
method type parameter conditions in companion objects:Tests:
UPDATE:
1) It is possible to collect all implicits in one object, and in this case object with implicits need to import (it is not needed before by rules of implicit scope in companion object):
and using:
2,3) trait
Conforms
defined for 2 type parameterX
&Y
X - used for future type constraint (and this constraint come from parametric method)
Y - used for determine the type for which will be define type constraint
implicit parameter choise by
Comforms
instance type, and idea of this design is playing with combinations X & Yin Type-class
TestableTypeClass
type Y captured by implicit conversion fromTestable
to anonimous class withtest
method, and type X captured intest
method call.And a main feature is invariance of
Conforms
trait, this is why implicits is not ambiguous and correctly manage bound rules.And for better understanding, one more example with more strict rules: