Created
July 31, 2013 16:35
-
-
Save seanparsons/6123697 to your computer and use it in GitHub Desktop.
Automatic tuple conversion powered by macros.
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
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] | |
} | |
} |
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
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)] | |
} | |
} | |
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
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