Skip to content

Instantly share code, notes, and snippets.

@OlegIlyenko
Created January 19, 2014 18:35
Show Gist options
  • Save OlegIlyenko/8508980 to your computer and use it in GitHub Desktop.
Save OlegIlyenko/8508980 to your computer and use it in GitHub Desktop.
Advanced Type Constraints with Type Classes.
import scala.reflect.runtime.universe.TypeTag
import scala.annotation.implicitNotFound
@implicitNotFound("Sorry, type inference was unable to figure out the type. You need to provide it explicitly.")
trait NotNothing[T]
object NotNothing {
private val evidence: NotNothing[Any] = new Object with NotNothing[Any]
implicit def notNothingEvidence[T](implicit n: T =:= T): NotNothing[T] =
evidence.asInstanceOf[NotNothing[T]]
}
def inject[T](implicit tt: TypeTag[T], nn: NotNothing[T]) =
tt.toString
inject
// <console>:12: error: Sorry, type inference was unable to figure out the type. You need to provide it explicitly.
// inject
// ^
inject[Nothing]
// <console>:12: error: Sorry, type inference was unable to figure out the type. You need to provide it explicitly.
// inject[Nothing]
// ^
val foo: String = inject
// <console>:11: error: Sorry, type inference was unable to figure out the type. You need to provide it explicitly.
// val foo: String = inject
// ^
inject[String]
// res6: String = TypeTag[String]
inject[Int]
// res7: String = TypeTag[Int]
def inject[T](implicit tt: TypeTag[T], nn: Not[T =:= Nothing]) =
tt.toString
inject
// <console>:11: error: Argument does not satisfy constraints: Not =:=[T,Nothing]
// inject
// ^
inject[Nothing]
// <console>:11: error: Argument does not satisfy constraints: Not =:=[Nothing,Nothing]
// inject[Nothing]
// ^
val foo: String = inject
// <console>:10: error: Argument does not satisfy constraints: Not =:=[T,Nothing]
// val foo: String = inject
// ^
inject[String]
// res3: String = TypeTag[String]
inject[Int]
// res4: String = TypeTag[Int]
def union1[T](t: T)(implicit c: (T =:= String) Or (T =:= Int)) = t match {
case s: String => println(s"Some nice string: $s")
case i: Int => println(s"Some int: $i")
}
type V[A, B] = {type l[T] = (T <:< A) Or (T <:< B)}
def union[T: (String V Int)#l](t: T) = t match {
case s: String => println(s"Some nice string: $s")
case i: Int => println(s"Some int: $i")
}
union("Test")
// Some nice string: Test
union(265)
// Some int: 265
union(2.0)
// <console>:11: error: Argument does not satisfy constraints: =:=[Double,String] Or =:=[Double,Int]
// union(2.0)
// ^
def sort[T: Ordering](list: List[T])(implicit c: Not[(T =:= String) Or Numeric[T]]) =
list.sorted
sealed trait Color
case object Red extends Color
case object Green extends Color
case object Blue extends Color
case object Yellow extends Color
case object Magenta extends Color
def printColor1[T](t: T)(implicit c: (T =:= Red.type) Or (T =:= Green.type) Or (T =:= Magenta.type)) =
println(t)
def printColor[T](t: T)(implicit c: (T <:< Color) And Not[T =:= Color] And Not[(T =:= Yellow.type) Or (T =:= Blue.type)]) =
println(t)
printColor(Yellow)
// <console>:11: error: Argument does not satisfy constraints: And[<:<[Yellow.type,Color],Not[=:=[Yellow.type,Color]]] And Not[Or[=:=[Yellow.type,Yellow.type],=:=[Yellow.type,Blue.type]]]
// printColor(Yellow)
// ^
val c: Color = Magenta
// c: Color = Magenta
printColor(c)
// <console>:12: error: Argument does not satisfy constraints: And[<:<[Color,Color],Not[=:=[Color,Color]]] And Not[Or[=:=[Color,Yellow.type],=:=[Color,Blue.type]]]
// printColor(c)
// ^
printColor(Magenta)
// Magenta
import scala.annotation.implicitNotFound
sealed trait Existence
trait Exists extends Existence
trait NotExists extends Existence
trait IsTypeClassExists[TypeClass, Answer]
object IsTypeClassExists {
private val evidence: IsTypeClassExists[Any, Any] =
new Object with IsTypeClassExists[Any, Any]
implicit def typeClassExistsEv[TypeClass, Answer](implicit a: TypeClass) =
evidence.asInstanceOf[IsTypeClassExists[TypeClass, Exists]]
implicit def typeClassNotExistsEv[TypeClass, Answer] =
evidence.asInstanceOf[IsTypeClassExists[TypeClass, NotExists]]
}
@implicitNotFound("Argument does not satisfy constraints: Not ${T}")
trait Not[T]
object Not {
private val evidence: Not[Any] = new Object with Not[Any]
implicit def notEv[T, Answer](implicit a: IsTypeClassExists[T, Answer], ne: Answer =:= NotExists) =
evidence.asInstanceOf[Not[T]]
}
@implicitNotFound("Argument does not satisfy constraints: ${A} And ${B}")
trait And[A, B]
object And {
private val evidence: And[Any, Any] = new Object with And[Any, Any]
implicit def bothExistEv[A, B](implicit a: A, b: B) =
evidence.asInstanceOf[And[A, B]]
}
@implicitNotFound("Argument does not satisfy constraints: ${A} Or ${B}")
trait Or[A, B]
object Or {
private val evidence: Or[Any, Any] = new Object with Or[Any, Any]
implicit def aExistsEv[A, B](implicit a: A) =
evidence.asInstanceOf[Or[A, B]]
implicit def bExistsEv[A, B](implicit b: B) =
evidence.asInstanceOf[Or[A, B]]
}
@OlegIlyenko
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment