I want to do something like this:
def iDontLikeStrings(arg: Not[String]) = {....}
Basically, this should compile:
iDontLikeStrings(23)
iDontLikeStrings(true)
And this should NOT compile:
iDontLikeStrings("hello")
Here is my implementation (see gist):
Step 1: Encoding to capture A is not a subtype of B
trait NotSubTypeOf[A, B]
Note: We can use infix notation to write A NotSubTypeOf B
instead of NotSubTypeOf[A, B]
Step 2: Evidence for any two arbitrary types A and B, A is not a subtype of B
implicit def isSub[A, B]: A NotSubTypeOf B = null
Step 3: Define ambigious implicits to trigger compile error in case A is a subtype of B (or A =:= B)
implicit def iSubAmbig1[A, B >: A]: A NotSubTypeOf B = null
implicit def iSubAmbig2[A, B >: A]: A NotSubTypeOf B = null
Step 4: Define a type-lambda for the negation type:
type Not[T] = {
type L[U] = U NotSubTypeOf T
}
With kind-projector, this can be made much more readable.
Step 5: Done!
def iDontLikeStrings[A: Not[String]#L](a: A) = {
println(a)
}
iDontLikeStrings(23) // compiles
iDontLikeStrings(true) // compiles
//iDontLikeStrings("hello") // does not compile
The compiler error message for the last line can be made better in Scala 2.12 which addresses SI-6806.
Also, all of this is built into Shapeless.