Skip to content

Instantly share code, notes, and snippets.

@clhodapp
Last active December 15, 2015 17:39
Show Gist options
  • Save clhodapp/5297934 to your computer and use it in GitHub Desktop.
Save clhodapp/5297934 to your computer and use it in GitHub Desktop.
Most of the macros needed to create a single tuple class that could hold an arbitrary number of items and retain typesafety
// This is an example usage
object Test extends App {
import Macros._
println(countRefs[Int~Int~String~List[Int]~Double]) // prints 2
println(countInts[Int~Int~String~List[Int]~Double]) // prints 2
println(countDoubles[Int~Int~String~List[Int]~Double]) // prints 1
println(whichArray[Int~Int~String~List[Int]~Double]("_0")) // prints Ints
println(whichArray[Int~Int~String~List[Int]~Double]("_2")) // prints References
println(whichArray[Int~Int~String~List[Int]~Double]("_4")) // prints Doubles
println(whichIndex[Int~Int~String~List[Int]~Double]("_0")) // prints 0
println(whichIndex[Int~Int~String~List[Int]~Double]("_1")) // prints 1
println(whichIndex[Int~Int~String~List[Int]~Double]("_2")) // prints 0 (started over b/c in references)
println(whichIndex[Int~String~Int]("_2")) // prints 1
println(whichIndex[Int~Int~Int]("_2")) // prints 2
}
import language.experimental.macros
import reflect.macros._
sealed trait ~[+A, +B]
sealed trait Arrays
case object References extends Arrays
case object Units extends Arrays
case object Booleans extends Arrays
case object Bytes extends Arrays
case object Chars extends Arrays
case object Shorts extends Arrays
case object Ints extends Arrays
case object Longs extends Arrays
case object Floats extends Arrays
case object Doubles extends Arrays
object Macros {
def countRefsImpl[T: c.WeakTypeTag](c: Context): c.Expr[Int] = {
import c.universe._
val primitiveValueTypes = Set(
typeOf[Unit],
typeOf[Boolean],
typeOf[Byte],
typeOf[Char],
typeOf[Short],
typeOf[Int],
typeOf[Long],
typeOf[Float],
typeOf[Double]
)
val tildeType = weakTypeOf[~[_, _]].typeConstructor
def isTildeType(tpe: Type) = tpe.erasure =:= tildeType.erasure
var current = weakTypeOf[T]
var count = 0
while (isTildeType(current)) {
val TypeRef(_, _, List(first, second)) = current
if (!primitiveValueTypes.exists(_ =:= second)) count += 1
current = first
}
if (!primitiveValueTypes.exists(_ =:= current)) count += 1
c.Expr[Int](Literal(Constant(count)))
}
def countUnitsImpl[T: c.WeakTypeTag](c: Context): c.Expr[Int] = {
import c.universe._
val tildeType = weakTypeOf[~[_, _]].typeConstructor
val unitType = typeOf[Double]
def isTildeType(tpe: Type) = tpe.erasure =:= tildeType.erasure
var current = weakTypeOf[T]
var count = 0
while (isTildeType(current)) {
val TypeRef(_, _, List(first, second)) = current
if (second <:< unitType) count += 1
current = first
}
if (current <:< unitType) count += 1
c.Expr[Int](Literal(Constant(count)))
}
def countBooleansImpl[T: c.WeakTypeTag](c: Context): c.Expr[Int] = {
import c.universe._
val tildeType = weakTypeOf[~[_, _]].typeConstructor
val booleanType = typeOf[Double]
def isTildeType(tpe: Type) = tpe.erasure =:= tildeType.erasure
var current = weakTypeOf[T]
var count = 0
while (isTildeType(current)) {
val TypeRef(_, _, List(first, second)) = current
if (second <:< booleanType) count += 1
current = first
}
if (current <:< booleanType) count += 1
c.Expr[Int](Literal(Constant(count)))
}
def countBytesImpl[T: c.WeakTypeTag](c: Context): c.Expr[Int] = {
import c.universe._
val tildeType = weakTypeOf[~[_, _]].typeConstructor
val byteType = typeOf[Byte]
def isTildeType(tpe: Type) = tpe.erasure =:= tildeType.erasure
var current = weakTypeOf[T]
var count = 0
while (isTildeType(current)) {
val TypeRef(_, _, List(first, second)) = current
if (second <:< byteType) count += 1
current = first
}
if (current <:< byteType) count += 1
c.Expr[Int](Literal(Constant(count)))
}
def countCharsImpl[T: c.WeakTypeTag](c: Context): c.Expr[Int] = {
import c.universe._
val tildeType = weakTypeOf[~[_, _]].typeConstructor
val charType = typeOf[Char]
def isTildeType(tpe: Type) = tpe.erasure =:= tildeType.erasure
var current = weakTypeOf[T]
var count = 0
while (isTildeType(current)) {
val TypeRef(_, _, List(first, second)) = current
if (second <:< charType) count += 1
current = first
}
if (current <:< charType) count += 1
c.Expr[Int](Literal(Constant(count)))
}
def countShortsImpl[T: c.WeakTypeTag](c: Context): c.Expr[Int] = {
import c.universe._
val tildeType = weakTypeOf[~[_, _]].typeConstructor
val shortType = typeOf[Short]
def isTildeType(tpe: Type) = tpe.erasure =:= tildeType.erasure
var current = weakTypeOf[T]
var count = 0
while (isTildeType(current)) {
val TypeRef(_, _, List(first, second)) = current
if (second <:< shortType) count += 1
current = first
}
if (current <:< shortType) count += 1
c.Expr[Int](Literal(Constant(count)))
}
def countIntsImpl[T: c.WeakTypeTag](c: Context): c.Expr[Int] = {
import c.universe._
val tildeType = weakTypeOf[~[_, _]].typeConstructor
val intType = typeOf[Int]
def isTildeType(tpe: Type) = tpe.erasure =:= tildeType.erasure
var current = weakTypeOf[T]
var count = 0
while (isTildeType(current)) {
val TypeRef(_, _, List(first, second)) = current
if (second <:< intType) count += 1
current = first
}
if (current <:< intType) count += 1
c.Expr[Int](Literal(Constant(count)))
}
def countLongsImpl[T: c.WeakTypeTag](c: Context): c.Expr[Int] = {
import c.universe._
val tildeType = weakTypeOf[~[_, _]].typeConstructor
val longType = typeOf[Double]
def isTildeType(tpe: Type) = tpe.erasure =:= tildeType.erasure
var current = weakTypeOf[T]
var count = 0
while (isTildeType(current)) {
val TypeRef(_, _, List(first, second)) = current
if (second <:< longType) count += 1
current = first
}
if (current <:< longType) count += 1
c.Expr[Int](Literal(Constant(count)))
}
def countFloatsImpl[T: c.WeakTypeTag](c: Context): c.Expr[Int] = {
import c.universe._
val tildeType = weakTypeOf[~[_, _]].typeConstructor
val floatType = typeOf[Float]
def isTildeType(tpe: Type) = tpe.erasure =:= tildeType.erasure
var current = weakTypeOf[T]
var count = 0
while (isTildeType(current)) {
val TypeRef(_, _, List(first, second)) = current
if (second <:< floatType) count += 1
current = first
}
if (current <:< floatType) count += 1
c.Expr[Int](Literal(Constant(count)))
}
def countDoublesImpl[T: c.WeakTypeTag](c: Context): c.Expr[Int] = {
import c.universe._
val tildeType = weakTypeOf[~[_, _]].typeConstructor
val doubleType = typeOf[Double]
def isTildeType(tpe: Type) = tpe.erasure =:= tildeType.erasure
var current = weakTypeOf[T]
var count = 0
while (isTildeType(current)) {
val TypeRef(_, _, List(first, second)) = current
if (second <:< doubleType) count += 1
current = first
}
if (current <:< doubleType) count += 1
c.Expr[Int](Literal(Constant(count)))
}
def whichArrayImpl[T: c.WeakTypeTag](c: Context)(name: c.Expr[String]) = {
import c.universe._
val Literal(Constant(value)) = name.tree
if (value.toString.head != '_') throw new IllegalArgumentException()
val index = value.toString.tail.toInt
val tildeType = weakTypeOf[~[_, _]].typeConstructor
def isTildeType(tpe: Type) = tpe.erasure =:= tildeType.erasure
def listify(tpe: Type, current: List[Type]): List[Type] = tpe match {
case TypeRef(_, _, List(first, second)) if isTildeType(tpe) => listify(first, second :: current)
case end => end :: current
}
val list = listify(weakTypeOf[T], Nil)
val chosen = list(index)
if (chosen =:= typeOf[Int]) reify{ Ints }
else if (chosen =:= typeOf[Unit]) reify { Units }
else if (chosen =:= typeOf[Boolean]) reify { Booleans }
else if (chosen =:= typeOf[Char]) reify { Chars }
else if (chosen =:= typeOf[Short]) reify { Shorts }
else if (chosen =:= typeOf[Long]) reify { Longs }
else if (chosen =:= typeOf[Float]) reify { Floats }
else if (chosen =:= typeOf[Double]) reify { Doubles }
else reify { References }
}
def whichIndexImpl[T: c.WeakTypeTag](c: Context)(name: c.Expr[String]): c.Expr[Int] = {
import c.universe._
val Literal(Constant(value)) = name.tree
if (value.toString.head != '_') throw new IllegalArgumentException()
val index = value.toString.tail.toInt
val tildeType = weakTypeOf[~[_, _]].typeConstructor
def isTildeType(tpe: Type) = tpe.erasure =:= tildeType.erasure
def listify(tpe: Type, current: List[Type]): List[Type] = tpe match {
case TypeRef(_, _, List(first, second)) if isTildeType(tpe) => listify(first, second :: current)
case end => end :: current
}
val list = listify(weakTypeOf[T], Nil)
val chosen = list(index)
val primitiveValueTypes = Set(
typeOf[Unit],
typeOf[Boolean],
typeOf[Byte],
typeOf[Char],
typeOf[Short],
typeOf[Int],
typeOf[Long],
typeOf[Float],
typeOf[Double]
)
if (primitiveValueTypes.exists(_ =:= chosen)) c.Expr[Int](Literal(Constant(list.take(index).count(_ =:= chosen))))
else c.Expr[Int](Literal(Constant(list.take(index).count(x => !primitiveValueTypes.exists(_ =:= x)))))
}
def countRefs[T] = macro countRefsImpl[T]
def countUnits[T] = macro countUnitsImpl[T]
def countBooleans[T] = macro countBooleansImpl[T]
def countChars[T] = macro countCharsImpl[T]
def countBytes[T] = macro countBytesImpl[T]
def countShorts[T] = macro countShortsImpl[T]
def countInts[T] = macro countIntsImpl[T]
def countLongs[T] = macro countLongsImpl[T]
def countFloats[T] = macro countFloatsImpl[T]
def countDoubles[T] = macro countDoublesImpl[T]
def whichArray[T](name: String) = macro whichArrayImpl[T]
def whichIndex[T](name: String) = macro whichIndexImpl[T]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment