Skip to content

Instantly share code, notes, and snippets.

@non
Created June 28, 2012 16:25
Show Gist options
  • Select an option

  • Save non/3012292 to your computer and use it in GitHub Desktop.

Select an option

Save non/3012292 to your computer and use it in GitHub Desktop.
Example of 2.10.0-M4 macros with typeclasses
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