Skip to content

Instantly share code, notes, and snippets.

@dgouyette
Last active July 4, 2016 11:45
Show Gist options
  • Save dgouyette/e5867d2411fe01459d9dbbc6e076841f to your computer and use it in GitHub Desktop.
Save dgouyette/e5867d2411fe01459d9dbbc6e076841f to your computer and use it in GitHub Desktop.
import scala.reflect.macros.blackbox
import scala.language.experimental.macros
import scala.annotation.StaticAnnotation
object union {
def impl(c: blackbox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
def argByXY(idx: Int, i: Int) = if (idx == i) {
q"""Some(t)"""
}
else {
q"""None"""
}
val result = {
annottees.map(_.tree).toList match {
case q"class $name[..$tpes](..$lFields) extends ..$parents { ..$body }" :: Nil =>
val typesName: Seq[c.universe.TypeName] = tpes.map { case TypeDef(_, typeName, _, _) => typeName }
q"""
class $name[..$tpes](..$lFields) extends ..$parents {
..$body
..${
typesName.zipWithIndex.map { case (currentTypeName, y) =>
val currentType2Union = TermName("toUnion" + y)
val unionTermName = TermName("Union" + tpes.size)
val constructorArgs = for (x <- typesName.indices) yield q"""${argByXY(x, y)}"""
q"""
implicit def $currentType2Union(t: $currentTypeName) = $unionTermName[..$typesName](..$constructorArgs);
"""
}
}
}
"""
}
}
println(result)
c.Expr[Any](result)
}
}
class union extends StaticAnnotation {
def macroTransform(annottees: Any*) = macro union.impl
}
//will generate the following code
/**
* class Union2Type[T1, T2] extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
implicit def toUnion0(t: T1) = Union2[T1, T2](Some(t), None);
implicit def toUnion1(t: T2) = Union2[T1, T2](None, Some(t))
}
class Union3Type[T1, T2, T3] extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
implicit def toUnion0(t: T1) = Union3[T1, T2, T3](Some(t), None, None);
implicit def toUnion1(t: T2) = Union3[T1, T2, T3](None, Some(t), None);
implicit def toUnion2(t: T3) = Union3[T1, T2, T3](None, None, Some(t))
}
class Union4Type[T1, T2, T3, T4] extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
implicit def toUnion0(t: T1) = Union4[T1, T2, T3, T4](Some(t), None, None, None);
implicit def toUnion1(t: T2) = Union4[T1, T2, T3, T4](None, Some(t), None, None);
implicit def toUnion2(t: T3) = Union4[T1, T2, T3, T4](None, None, Some(t), None);
implicit def toUnion3(t: T4) = Union4[T1, T2, T3, T4](None, None, None, Some(t))
}**/
@vil1
Copy link

vil1 commented Jul 4, 2016

I wonder if it would be possible to get unions without "wasting" memory like the solution proposed here.
Where can I find an example of usage of these generated UnionX types ?

@dgouyette
Copy link
Author

You can find the full version here : https://github.com/dgouyette/UnionMacroSample

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