Skip to content

Instantly share code, notes, and snippets.

@fuCtor
Created June 22, 2017 06:05
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save fuCtor/5284fd15fcb349825238c028341bd0a1 to your computer and use it in GitHub Desktop.
Scala, проба работы с implicit + tagged type + annotation
import scala.annotation.StaticAnnotation
import scala.language.experimental.macros
import scala.reflect.macros.whitebox.Context
final class currency extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro currencyMacro.impl
}
object currencyMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
def addTree(tpl: Template, tree: Tree): Template = addTreeList(tpl, List(tree))
def addTreeList(tpl: Template, trees: List[Tree]): Template =
Template(tpl.parents, tpl.self, trees ::: tpl.body)
val inputs = annottees.map(_.tree).toList
val expandees = inputs.head match {
case param@ModuleDef(mods, name, tpl) =>
val currencyName = s" ${name.decodedName.toString}"
val ops =
q"""
implicit class Ops(val __v:Type) extends AnyVal {
def pretty: String = __v.toString + $currencyName
}
"""
val tn = name.decodedName.toTypeName
val typ = q"type $tn = $name.Type"
val fn = TermName(s"doubleTo${name.decodedName}")
val cast = q"implicit def $fn(x : Double) : $tn = $name(x)"
val newParam = ModuleDef(mods, name, addTree(tpl, ops))
List(newParam, typ, cast)
case _ =>
inputs
}
val outputs = expandees
c.Expr[Any](Block(outputs, Literal(Constant(()))))
}
}
final class invertRate extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro invertRateMacro.impl
}
object invertRateMacro {
def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._
val inputs = annottees.map(_.tree).toList
val expandees = inputs.head match {
case param@ValDef(mods, name, t1, t2) =>
val invertedFun = t2 match {
case Apply(fun, List(v)) =>
val TypeApply(fName, List(dt, from, to)) = fun
val invertedFName = TypeApply(fName, List(dt, to, from))
Apply(invertedFName, List(q"1.0/$v"))
}
val newValName = TermName(s"${name.decodedName.toString}Inv")
val newVal = q"implicit val $newValName = $invertedFun"
List(param, newVal)
}
val outputs = expandees
c.Expr[Any](Block(outputs, Literal(Constant(()))))
}
}
import supertagged._
object Test extends App {
object currency {
trait Currency extends TaggedType[Double]
@currency object Rub extends Currency
@currency object Usd extends Currency
@currency object Eur extends Currency
}
import currency._
trait Rate[T, C1, C2] {
def apply(from : C1)(implicit m : Numeric[T], c : (T) => C2) : C2
}
def exchangeRate[T, C1, C2]( rate : T) = new Rate[T, C1, C2] {
def apply(from : C1)(implicit m : Numeric[T], c : (T) => C2): C2 = m.times(from.asInstanceOf[T], rate : T)
}
implicit class Converter[C1, T](val arg1: T @@ C1) extends AnyVal {
def ++[C2](arg2 : T @@ C2)(implicit r : Rate[T, T @@ C1, T @@ C2], m : Fractional[T], f1 : (T) => T @@ C1, f2 : (T) => T @@ C2 ) = f1(m.plus(arg1, r(arg2)))
def --[C2](arg2 : T @@ C2)(implicit r : Rate[T, T @@ C1, T @@ C2], m : Fractional[T], f1 : (T) => T @@ C1, f2 : (T) => T @@ C2 ) = f1(m.minus(arg1, r(arg2)))
}
implicit val rubToRub = exchangeRate[Double, Rub, Rub](1)
implicit val usdToUsd = exchangeRate[Double, Usd, Usd](1)
@invertRate implicit val rubToUsd = exchangeRate[Double, Rub, Usd](32)
@invertRate implicit val rubToEur = exchangeRate[Double, Rub, Eur](60)
val c1 : Rub = Rub @@ 40.0
val c2 : Usd = Usd @@ 1.0
val c3 : Eur = Eur @@ 1.0
println(c1 pretty)
println(c2 pretty)
println((c1 ++ c2) pretty)
println((c1 -- c2) pretty)
println((c3 ++ c1) pretty)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment