Skip to content

Instantly share code, notes, and snippets.

@seanparsons
Created July 31, 2013 16:35
Show Gist options
  • Save seanparsons/6123697 to your computer and use it in GitHub Desktop.
Save seanparsons/6123697 to your computer and use it in GitHub Desktop.
Automatic tuple conversion powered by macros.
package myproject.util
import myproject.macros._
import scalaz._,Scalaz._,Kleisli._
object KleisliEnhance {
implicit class KleisliEnhancer[M[+_]: Monad, A, B](kleisli: Kleisli[M, A, B]) {
def localFrom[AA]: Kleisli[M, AA, B] = kleisli.local(TupleTransformer.tupleToFrom[A, AA]
}
}
package myproject
import myproject.util.KleisliEnhance._
object MacroUsage {
def test() {
// [error] MacroUsage.scala:8: AA must be a TupleN type, found myproject.util.KleisliEnhance.KleisliEnhancer.AA instead.
createKleisli[M, (T, U)](from, to).localFrom[(T, U, V)]
}
}
package myproject.macros
import scala.reflect.macros.Context
import scalaz._,Scalaz._,Kleisli._
object TupleTransformer {
def tupleToFrom[A, AA]: AA => A = macro tupleToFromImpl[A, AA]
def tupleToFromImpl[A: c.WeakTypeTag, AA: c.WeakTypeTag](c: Context): c.Expr[AA => A] = {
import c.universe._
val aType: Type = weakTypeOf[A]
val aaType: Type = weakTypeOf[AA]
// Get constituent types of AA in order, checking it is a TupleN type.
println(aaType.baseClasses)
if (!aaType.widen.normalize.typeSymbol.fullName.startsWith("scala.Tuple")) {
c.abort(c.enclosingPosition, "AA must be a TupleN type, found %s instead.".format(aaType.widen.typeSymbol.fullName))
}
val aaMemberTypes: List[Type] = (1 to aaType.typeSymbol.fullName.replace("scala.Tuple", "").toInt).toList.map{n =>
aaType.members.find(_.name.decoded == "_" + n).get.typeSignature
}
if (aaMemberTypes.contains(aType)) {
// If A is one of the consituent types of the tuple.
// Create function indexing to that specific type.
c.Expr[AA => A](Function(
List(ValDef(Modifiers(Flag.PARAM), newTermName("tuple"), TypeTree(aaType), EmptyTree)),
Select(Ident(newTermName("tuple")), newTermName("_" + (aaMemberTypes.indexOf(aType) + 1).toString))
))
} else {
// Or that it a TupleN type consisting only of types available in AA.
// Create function returning a tuple which
c.Expr[AA => A](Function(
List(ValDef(Modifiers(Flag.PARAM), newTermName("tuple"), TypeTree(aaType), EmptyTree)),
Select(Ident(newTermName("tuple")), newTermName("_1"))
))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment