Skip to content

Instantly share code, notes, and snippets.

@retronym
Created November 25, 2009 13:01
Show Gist options
  • Save retronym/242689 to your computer and use it in GitHub Desktop.
Save retronym/242689 to your computer and use it in GitHub Desktop.
Type Relations in scala
/**
* Type Equivalence Bound
*/
sealed abstract class =:=[From, To] extends (From => To) {
override def toString = "=:=[From, To]"
}
object =:= {
implicit def typeEquals[A]: A =:= A = new (A =:= A) {
def apply(x: A) = x
}
}
/**
* Type Conformance Bound
*/
sealed abstract class <:<[-From, +To] extends (From => To)
object <:< {
implicit def conforms[A]: A <:< A = new (A <:< A) {
def apply(x: A) = x
}
}
/**
* Type View Bound
*/
sealed abstract class <%<[-From, +To] {
def view(f: From): To
}
object <%< {
implicit def viewableAs[To, From <% To]: <%<[From, To] = new (From <%< To) {
def view(x: From): To = x
}
}
trait TypeRelationLowPriority {
implicit def defaultTypeEquals[From, To]: Option[From =:= To] = None
implicit def defaultConformance[From, To]: Option[From <:< To] = None
implicit def defaultView[From, To]: Option[From <%< To] = None
}
object TypeRelation extends TypeRelationLowPriority {
implicit def typeEqualsOption[A](implicit check: A =:= A): Option[A =:= A] = Some(check)
implicit def conformsOption[A](implicit check: A <:< A): Option[A <:< A] = Some(check)
implicit def viewableAsOption[To, From <% To]: Option[From <%< To] = Some(<%<.viewableAs[To, From])
def typeEquals[From, To](implicit check: Option[From =:= To]) = check.isDefined
def conforms[From, To](implicit check: Option[From <:< To]) = check.isDefined
def viewableAs[From, To](implicit check: Option[From <%< To]) = check.isDefined
}
import TypeRelation._
// Compile time checks that give false if bound not satisfied.
{
assert(!typeEquals[String, Boolean], "!typeEquals[String, Boolean]")
assert(typeEquals[String, String], "typeEquals[String, String]")
assert(conforms[Int, Any])
assert(!conforms[Any, Int])
assert(conforms[List[Int], List[_]])
assert(!viewableAs[Boolean, Int])
{
implicit def bool2String(b: Boolean): String = b.toString
assert(viewableAs[Boolean, String])
}
}
// Compile Time Checks that give compile errors if bound not satisfied.
implicitly[String =:= String]
implicitly[String <:< AnyRef]
implicitly[Option[Int] <%< Iterable[Int]]
// Using different type bounds
class ListW[A](l: List[A]) {
def flattenFunction[B](implicit ev: A => Traversable[B]): List[B] = l.flatten
/**
* Flatten a List[List[B]] to List[B]
*/
def flattenStrict[B](implicit ev: A =:= List[B]): List[B] = l.flatten
/**
* Flatten a List[+Traversable[B]] to List[B]
*/
def flattenConforms[B](implicit ev: A <:< Traversable[B]): List[B] = l.flatten
/**
* Flatten a List[A] to List[B], where A can be viewed as Traversable[B]
*/
def flattenView[B](implicit ev: A <%< Traversable[B]): List[B] = l.flatten(ev.view _)
}
implicit def List2ListW[A](list: List[A]): ListW[A] = new ListW[A](list)
List(Some(1), None).flattenFunction
List(Seq(1)).flattenConforms
List(List(1)).flattenStrict[Int] // without explicit type arg: "error: could not find implicit value for parameter ev: this.=:=[List[Int],List[B]]"
List(Some(1), None).flattenView[Int] // without explicit type arg: "error: could not find implicit value for parameter ev: this.<%<[Option[Int],Traversable[B]]"
// Maybe related to the implicit type / type inference limitation described:
// https://lampsvn.epfl.ch/trac/scala/ticket/2673#comment:3
// Crazy idea for runtime implicit conversions for arbitrary types. This would only be able to use the
// erased type on the unknown object, but still might be useful...
object implicitmanifest {
class ImplicitScopeManifest[T] {
def findView[From](from: ClassManifest[From])(implicit to: Manifest[T]): Option[From => T] = {
// search list of implicits for matching function, as per Implicits.scala in the compiler.
None
}
}
// The compiler would capture a manifest of all implicit conversions in scope at the call site, including conversions provided by type T
def view[From <: AnyRef, T](a: From)(implicit to: Manifest[T], implicitScopeManifest: ImplicitScopeManifest[T]): Option[(From => T)] = {
val classManifest = scala.reflect.ClassManifest.fromClass[From](a.getClass().asInstanceOf[Class[From]])
implicitScopeManifest.findView[From](classManifest)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment