Created
November 25, 2009 13:01
-
-
Save retronym/242689 to your computer and use it in GitHub Desktop.
Type Relations in scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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