Enforce type difference
I have a simpler solution, which also leverages ambiguity,
trait =!=[A, B]
implicit def neq[A, B] : A =!= B = null
// This pair excludes the A =:= B case
implicit def neqAmbig1[A] : A =!= A = null
implicit def neqAmbig2[A] : A =!= A = null
The original use case,
case class Foo[A,B](a : A, b : B)(implicit ev: A =!= B)
new Foo(1, "1")
new Foo("foo", Some("foo"))
// These don't compile
// new Foo(1, 1)
// new Foo("foo", "foo")
// new Foo(Some("foo"), Some("foo"))
Update
We can relate this to my "magical typesystem tricks" (thanks @jpp ;-) as follows,
type ¬[T] = T => Nothing
implicit def neg[T, U](t : T)(implicit ev : T =!= U) : ¬[U] = null
def notString[T <% ¬[String]](t : T) = t
Sample REPL session,
scala> val ns1 = notString(1)
ns1: Int = 1
scala> val ns2 = notString(1.0)
ns2: Double = 1.0
scala> val ns3 = notString(Some("foo"))
ns3: Some[java.lang.String] = Some(foo)
scala> val ns4 = notString("foo")
<console>:14: error: No implicit view available from
java.lang.String => (String) => Nothing.
val ns4 = notString2("foo")
^
Riffing off of Jean-Philippe's ideas, this works:
sealed class =!=[A,B]
trait LowerPriorityImplicits {
implicit def equal[A]: =!=[A, A] = sys.error("should not be called")
}
object =!= extends LowerPriorityImplicits {
implicit def nequal[A,B](implicit same: A =:= B = null): =!=[A,B] =
if (same != null) sys.error("should not be called explicitly with same type")
else new =!=[A,B]
}
case class Foo[A,B](a: A, b: B)(implicit e: A =!= B)
Then:
// compiles:
Foo(1f, 1.0)
Foo("", 1.0)
Foo("", 1)
Foo("Fish", Some("Fish"))
// doesn't compile
// Foo(1f, 1f)
// Foo("", "")
I'd probably simplify this as follows, since the checks for "cheating" can always be circumvented anyway (e.g. Foo(1, 1)(null)
or =!=.nequal(null)
):
sealed class =!=[A,B]
trait LowerPriorityImplicits {
/** do not call explicitly! */
implicit def equal[A]: =!=[A, A] = sys.error("should not be called")
}
object =!= extends LowerPriorityImplicits {
/** do not call explicitly! */
implicit def nequal[A,B]: =!=[A,B] = new =!=[A,B]
}