Well, after accidentally beating the keyboard and reading as much as I could understand about type restrictions, this is what I came up with:
// A without B is C sealed abstract class isWithout[A, B, C] object Syntax { implicit def composedWithout[A <: C, B, C](implicit ev: C <:!< B): isWithout[A, B, C] = new isWithout[A, B, C] { def apply(a: A) = a } type without[A, B] = { type l[C] = isWithout[A, B, C] } }
Verify that this works:
implicitly[isWithout[POSCONST, POSITIVE, CONST]] implicitly[isWithout[POSINTCONST, DISCRETE, POSNUMCONST]] implicitly[isWithout[POSINTCONST, DISCRETE, POSITIVE]] implicitly[isWithout[POSNUM, CONST, POSNUM]] implicitly[isWithout[POSCONST, CONST, POSITIVE ]] implicitly[isWithout[POSCONST, POSITIVE, CONST ]] implicitly[isWithout[INTEGER, DISCRETE, NUMERIC ]] implicitly[isWithout[POSINTCONST, CONST, POSINT ]]
And it fails if necessary:
implicitly[isWithout[POSINTCONST, INTCONST, POSINTCONST]] implicitly[isWithout[NUMERIC, ANYSYNTAX, ANYSYNTAX]] implicitly[isWithout[INTEGER, POSITIVE, POSINT]] implicitly[isWithout[POSNUM, DISCRETE, POSCONST]]
implicitly compiler implicitly gets to search for an implicit function in the current implicit region that can create an object of the required type (in this case, an instance of the isWithout class). If it finds one that satisfies the type signature, then it compiles (I donβt think it matters that the apply method defined in the class returns). An important point is a type signature that uses the <:!< Mentioned in the question and is borrowed from another SO answer from Miles.
This type signature says: A is a subtype of C and C should not be a subtype of B. Alternative version (corresponding to the first definition in the question):
implicit def composedWithout[A <: C with B, B, C](implicit ev: C <:!< B): isWithout[A, B, C] = new isWithout[A, B, C] { def apply(a: A, b: B) = a }
This can be used in several ways:
To verify the correctness of the type hierarchy (for example, test cases), as shown when using the above implicitly .
To indicate the type of method parameter:
def test[R : without[POSINTCONST, DISCRETE]#l](v: R) = v
Note that this uses a projection of type without#l[C] = isWithout[A, B, C] as the context boundary that the compiler assigns as follows:
def test[R](v: R)(implicit $ev0: isWithout[POSINTCONST, DISCRETE, R]) = v
Thus, it requires the specified implicit to be in scope.
As a type restriction asked in the original question:
case class Subtract[A <: R, R <: A without POSITIVE]( arg1: Expression[A], arg2: Expression[A] ) extends BinaryPrimitive[A, R]( arg1, arg2 )
This compiles, although I admit that I have not run anything yet, so that he does not do what I think he does.