Skip to content

Instantly share code, notes, and snippets.

@msiegenthaler
Created June 22, 2011 21:11
Show Gist options
  • Save msiegenthaler/1041216 to your computer and use it in GitHub Desktop.
Save msiegenthaler/1041216 to your computer and use it in GitHub Desktop.
Union Type in Scala with some experimental functions on top of it (like toEither)
import annotation.implicitNotFound
package object union {
private type ¬[A] = A => Nothing
private type ¬¬[A] = ¬[¬[A]]
private type ∧[A, B] = A with B
private type ∨[A, B] = ¬[∧[¬[A], ¬[B]]] // since (A ∨ B) ⇔ ¬(¬A ∧ ¬B)
sealed trait UnionInstance[A, B, T, -From, +To] {
protected[this] val manifestA: Manifest[A]
protected[this] val manifestB: Manifest[B]
private def asA(v: T) = v match {
case v: A => v
}
private def asB(v: T) = v match {
case v: B => v
}
def fold[R](fa: A => R)(fb: B => R)(v: T) = {
v match {
case i: Int =>
if (manifestA >:> Manifest.Int) fa(asA(v))
else fb(asB(v))
case s: AnyRef =>
val manifestS = Manifest.singleType(s)
if (manifestA.erasure.isAssignableFrom(manifestS.erasure)) fa(asA(v))
else fb(asB(v))
}
}
object first {
def unapply(v: T) = fold[Option[A]](Some(_))(b => None)(v)
}
object second {
def unapply(v: T) = fold[Option[B]](a => None)(Some(_))(v)
}
val left = first
val right = second
def toEither(v: T): Either[A, B] = fold[Either[A, B]](Left(_))(Right(_))(v)
}
@implicitNotFound(msg = "${T} not either ${A} or ${B}")
implicit def unionInstance[T, I, A: Manifest, B: Manifest]: UnionInstance[A, B, T, I, I] = new UnionInstance[A, B, T, I, I]() {
override val manifestA = manifest[A]
override val manifestB = manifest[B]
}
/**
* Type T is union-type of A and B.
* Usage:
* def my[T](value: T)(implicit u: Union[T, Int, String])
*
* Features with u:
* - value match { case u.first(v) => v }
* - u.fold(_.toString)(_)(value)
* - u.toEither(value)
*/
type Union[T, A, B] = UnionInstance[A, B, T, ¬¬[T], ∨[A, B]]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment