Created
June 28, 2012 16:25
-
-
Save non/3012292 to your computer and use it in GitHub Desktop.
Example of 2.10.0-M4 macros with typeclasses
This file contains hidden or 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 ring | |
| import language.implicitConversions | |
| import language.experimental.macros | |
| import scala.{specialized => spec} | |
| import scala.reflect.makro.Context | |
| // fairly uncontroversial typeclass | |
| trait Ring[@spec(Int) A] { | |
| def zero: A | |
| def one: A | |
| def negate(x:A): A | |
| def plus(x:A, y:A): A | |
| def minus(x:A, y:A): A | |
| def times(x:A, y:A): A | |
| } | |
| // companion object providing typeclass instance, methods, and implicits | |
| object Ring { | |
| implicit object IntHasRing extends Ring[Int] { | |
| def zero = 0 | |
| def one = 1 | |
| def negate(x:Int) = -x | |
| def plus(x:Int, y:Int) = x + y | |
| def minus(x:Int, y:Int) = x - y | |
| def times(x:Int, y:Int) = x * y | |
| } | |
| @inline final def apply[@spec(Int) A](implicit ev:Ring[A]) = ev | |
| @inline final def zero[@spec(Int) A](implicit ev:Ring[A]) = ev.zero | |
| @inline final def one[@spec(Int) A](implicit ev:Ring[A]) = ev.one | |
| implicit class RingOps[A:Ring](x:A) { | |
| def unary_-() = macro RingMacros.negate[A] | |
| def negate() = macro RingMacros.negate[A] | |
| def +(y:A) = macro RingMacros.plus[A] | |
| def -(y:A) = macro RingMacros.minus[A] | |
| def *(y:A) = macro RingMacros.times[A] | |
| } | |
| } | |
| // macros used by ring typeclass | |
| object RingMacros extends Operators { | |
| def negate[A](c:Context)() = unop(c)("negate") | |
| def plus[A](c:Context)(y:c.Expr[A]) = binop(c)(y, "plus") | |
| def minus[A](c:Context)(y:c.Expr[A]) = binop(c)(y, "minus") | |
| def times[A](c:Context)(y:c.Expr[A]) = binop(c)(y, "times") | |
| } | |
| // generic trait for use with any macro-enabled Ops class | |
| trait Operators { | |
| // used with unary operators, e.g. !x, -x | |
| def unop[A](c:Context)(name:String):c.Expr[A] = { | |
| import c.universe._ | |
| c.prefix.tree match { | |
| case Apply(Apply(TypeApply(_, _), List(x)), List(ev)) => | |
| c.Expr[A](Apply(Select(ev, name), List(x))) | |
| case t => sys.error("bad tree: %s" format t) | |
| } | |
| } | |
| // used with binary operators, e.g. x + y, x min y | |
| def binop[A](c:Context)(y:c.Expr[A], name:String):c.Expr[A] = { | |
| import c.universe._ | |
| c.prefix.tree match { | |
| case Apply(Apply(TypeApply(_, _), List(x)), List(ev)) => | |
| c.Expr[A](Apply(Select(ev, name), List(x, y.tree))) | |
| case t => sys.error("bad tree: %s" format t) | |
| } | |
| } | |
| } | |
| // NOTE: uncomment "Test" once you've compiled the macros. | |
| // re-comment if necessary when editing macros. | |
| //// test out ring a bit | |
| //object Test { | |
| // import Ring._ | |
| // def test[@spec(Int) A:Ring](x:A, y:A, z:A) = { | |
| // val a:A = x * x + y * y | |
| // val b:A = a * z + zero[A] | |
| // val c:A = b + one[A] | |
| // //val d:A = -b // test.scala:73: error: type mismatch; | |
| // // // found : A | |
| // // // required: Nothing | |
| // a + b + c | |
| // } | |
| // def main(args:Array[String]): Unit = println(test(7, 5, 3)) | |
| //} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment