All Downloads are FREE. Search and download functionalities are using the official Maven repository.

spire.ops.Ops.scala Maven / Gradle / Ivy

The newest version!
package machinist

import scala.language.higherKinds

import scala.reflect.macros.blackbox.Context

/**
 * This trait has some nice methods for working with implicit `Ops`
 * classes. It is used to rewrite implicit conversions which "enrich"
 * a type with operators into code that does not allocate an implicit
 * instance.
 *
 * @groupname macros Macros
 * @groupdesc macros Macro transformations for operators
 */
trait Ops {

  /**
   * Given context, this method rewrites the tree to call the desired
   * method with the lhs parameter. We find the symbol which is
   * applying the macro and use its name to determine what method to
   * call.
   *
   * If we see code like:
   *
   * {{{
   *   -lhs
   * }}}
   *
   * After typing and implicit resolution, we get trees like:
   *
   * {{{
   *   conversion(lhs)(ev).unary_-(): R
   * }}}
   *
   * The macro should produce trees like:
   *
   * {{{
   *   ev.negate(lhs): R
   * }}}
   *
   * @group macros
   */
  def unop[R](c: Context)(): c.Expr[R] = {
    import c.universe._
    val (ev, lhs) = unpack(c)
    c.Expr[R](Apply(Select(ev, findMethodName(c)), List(lhs)))
  }

  /**
   * A variant of [[unop]] which doesn't take an empty parameter list.
   *
   * @group macros
   */
  def unop0[R](c: Context): c.Expr[R] = {
    import c.universe._
    val (ev, lhs) = unpack(c)
    c.Expr[R](Apply(Select(ev, findMethodName(c)), List(lhs)))
  }

  /**
   * Like [[unop]], but with ev provided to the method instead of to the
   * implicit constructor.
   *
   * If we see code like:
   *
   * {{{
   *   lhs.abs
   * }}}
   *
   * After typing and implicit resolution, we get trees like:
   *
   * {{{
   *   conversion(lhs).abs(ev: Ev): R
   * }}}
   *
   * The macro should produce trees like:
   *
   * {{{
   *   ev.abs(lhs): R
   * }}}
   *
   * @group macros
   */
  def unopWithEv[Ev, R](c: Context)(ev: c.Expr[Ev]): c.Expr[R] = {
    import c.universe._
    val lhs = unpackWithoutEv(c)
    c.Expr[R](Apply(Select(ev.tree, findMethodName(c)), List(lhs)))
  }

  /**
   * Like [[unop]] and [[unopWithEv]], but there is ev provided by the implicit
   * constructor, and ev1 provided by the method.
   *
   * If we see code like:
   *
   * {{{
   *   lhs.isId
   * }}}
   *
   * After typing and implicit resolution, we get trees like:
   *
   * {{{
   *   conversion(lhs)(ev: Ev).isId(ev1: Ev1): R
   * }}}
   *
   * The macro should produce trees like:
   *
   * {{{
   *   ev.isId(lhs)(ev1): R
   * }}}
   *
   * @group macros
   */
  def unopWithEv2[Ev1, R](c: Context)(ev1: c.Expr[Ev1]): c.Expr[R] = {
    import c.universe._
    val (ev, lhs) = unpack(c)
    c.Expr[R](Apply(Apply(Select(ev, findMethodName(c)), List(lhs)), List(ev1.tree)))
  }

  /**
   * Given context and an expression, this method rewrites the tree to
   * call the "desired" method with the lhs and rhs parameters. We
   * find the symbol which is applying the macro and use its name to
   * determine what method to call.
   *
   * If we see code like:
   *
   * {{{
   *   lhs + rhs
   * }}}
   *
   * After typing and implicit resolution, we get trees like:
   *
   * {{{
   *   conversion(lhs)(ev).\$plus(rhs: A): R
   * }}}
   *
   * The macro should produce trees like:
   *
   * {{{
   *   ev.method(lhs: A, rhs: A): R
   * }}}
   *
   * @group macros
   */
  def binop[A, R](c: Context)(rhs: c.Expr[A]): c.Expr[R] = {
    import c.universe._
    val (ev, lhs) = unpack(c)
    c.Expr[R](Apply(Select(ev, findMethodName(c)), List(lhs, rhs.tree)))
  }

  /**
   * Like [[binop]], but for right-associative operators (eg. `+:`).
   *
   * If we see code like:
   *
   * {{{
   *   lhs *: rhs
   * }}}
   *
   * After typing and implicit resolution, we get trees like:
   *
   * {{{
   *   conversion(rhs)(ev).\$times\$colon(lhs)
   * }}}
   *
   * The macro should produce trees like:
   *
   * {{{
   *   ev.timesl(lhs, rhs)
   * }}}
   *
   * @group macros
   */
  def rbinop[A, R](c: Context)(lhs: c.Expr[A]): c.Expr[R] = {
    import c.universe._
    val (ev, rhs) = unpack(c)
    c.Expr[R](Apply(Select(ev, findMethodName(c)), List(lhs.tree, rhs)))
  }

  def unopWithScalar[R](c: Context)(): c.Expr[R] =
    handleUnopWithChild[R](c)("scalar")

  def unopWithScalar0[R](c: Context): c.Expr[R] =
    handleUnopWithChild[R](c)("scalar")

  def handleUnopWithChild[R](c: Context)(childName: String): c.Expr[R] = {
    import c.universe._
    val (ev, lhs) = unpack(c)
    val child = Select(ev, TermName(childName))
    c.Expr[R](Apply(Select(child, findMethodName(c)), List(lhs)))
  }

  /**
   * Like [[binop]], but where the implementation comes from a child member
   *
   * If we see code like:
   *
   * {{{
   *   lhs * rhs
   * }}}
   *
   * After typing and implicit resolution, we get trees like:
   *
   * {{{
   *   conversion(lhs)(ev).\$times(rhs)
   * }}}
   *
   * The macro should produce trees like:
   *
   * {{{
   *   ev.scalar.times(lhs, rhs)
   * }}}
   *
   * @group macros
   */
  def binopWithScalar[A, R](c: Context)(rhs: c.Expr[A]): c.Expr[R] =
    handleBinopWithChild(c)(rhs)("scalar")

  /** Provided to make defining things like [[binopWithScalar]] easier.
   *
   * @group macros
   */
  def handleBinopWithChild[A, R](c: Context)(rhs: c.Expr[A])(childName: String): c.Expr[R] = {
    import c.universe._
    val (ev, lhs) = unpack(c)
    val child = Select(ev, TermName(childName))
    c.Expr[R](Apply(Select(child, findMethodName(c)), List(lhs, rhs.tree)))
  }

  /**
   * Like [[binop]], but with ev provided to the method instead of to the
   * implicit constructor.
   *
   * If we see code like:
   *
   * {{{
   *   lhs % rhs
   * }}}
   *
   * After typing and implicit resolution, we get trees like:
   *
   * {{{
   *   conversion(lhs).\$percent(rhs)(ev)
   * }}}
   *
   * The macro should produce trees like:
   *
   * {{{
   *   ev.mod(lhs, rhs)
   * }}}
   *
   * @group macros
   */
  def binopWithEv[A, Ev, R](c: Context)(rhs: c.Expr[A])(ev: c.Expr[Ev]): c.Expr[R] = {
    import c.universe._
    val lhs = unpackWithoutEv(c)
    c.Expr[R](Apply(Select(ev.tree, findMethodName(c)), List(lhs, rhs.tree)))
  }

  /**
   * Like [[rbinop]], but with ev provided to the method instead of to the
   * implicit constructor.
   *
   * If we see code like:
   *
   * {{{
   *   lhs *: rhs
   * }}}
   *
   * After typing and implicit resolution, we get trees like:
   *
   * {{{
   *   conversion(rhs).\$times\$colon(lhs)(ev)
   * }}}
   *
   * The macro should produce trees like:
   *
   * {{{
   *   ev.timesl(lhs, rhs)
   * }}}
   *
   * @group macros
   */
  def rbinopWithEv[A, Ev, R](c: Context)(lhs: c.Expr[A])(ev: c.Expr[Ev]): c.Expr[R] = {
    import c.universe._
    val rhs = unpackWithoutEv(c)
    c.Expr[R](Apply(Select(ev.tree, findMethodName(c)), List(lhs.tree, rhs)))
  }

  /**
   * Combine an implicit enrichment with a lifting method.
   *
   * If we see code like:
   *
   * {{{
   *   lhs + 1
   * }}}
   *
   * After typing and implicit resolution, we get trees like:
   *
   * {{{
   *   conversion(lhs)(ev0).\$plus(1)(ev1): R
   * }}}
   *
   * The macro should produce trees like:
   *
   * {{{
   *   ev0.plus(lhs, ev1.fromInt(1))
   * }}}
   *
   * In Spire, this lets us use `Ring`'s fromInt method and
   * `ConvertableTo`'s `fromDouble` (etc.) before applying an
   * op. Eventually, we should generalize the way we choose the
   * lifting method.
   *
   * @group macros
   */
  def binopWithLift[A: c.WeakTypeTag, Ev, R](c: Context)(rhs: c.Expr[A])(ev1: c.Expr[Ev]): c.Expr[R] = {
    import c.universe._
    val (ev0, lhs) = unpack(c)
    val typeName = weakTypeOf[A].typeSymbol.name
    val rhs1 = Apply(Select(ev1.tree, TermName("from" + typeName)), List(rhs.tree))
    c.Expr[R](Apply(Select(ev0, findMethodName(c)), List(lhs, rhs1)))
  }

  /**
   * This is like [[binopWithLift]], but we use the same evidence
   * parameter to make the method call and do the lifting.
   *
   * If we see code like:
   *
   * {{{
   *   lhs * 2
   * }}}
   *
   * After typing and implicit resolution, we get trees like:
   *
   * {{{
   *   conversion(lhs)(ev).\$times(2): R
   * }}}
   *
   * The macro should produce trees like:
   *
   * {{{
   *   ev.times(lhs, ev.fromInt(2))
   * }}}
   *
   * @group macros
   */
  def binopWithSelfLift[A: c.WeakTypeTag, Ev, R](c: Context)(rhs: c.Expr[A]): c.Expr[R] = {
    import c.universe._
    val (ev0, lhs) = unpack(c)
    val typeName = weakTypeOf[A].typeSymbol.name
    val rhs1 = Apply(Select(ev0, TermName("from" + typeName)), List(rhs.tree))
    c.Expr[R](Apply(Select(ev0, findMethodName(c)), List(lhs, rhs1)))
  }

  /**
   * Similar to [[binop]], but for situations where there is no evidence
   * parameter, and we just want to call a method on the rhs.
   *
   * After typing and implicit resolution, we get trees like:
   *
   * {{{
   *   conversion(lhs).foo(rhs)
   * }}}
   *
   * and we want to get out:
   *
   * {{{
   *   rhs.foo(lhs)
   * }}}
   *
   * @group macros
   */
  def flip[A, R](c: Context)(rhs: c.Expr[A]): c.Expr[R] = {
    import c.universe._
    val lhs = unpackWithoutEv(c)
    c.Expr[R](Apply(Select(rhs.tree, findMethodName(c)), List(lhs)))
  }

  /**
   * Given context, this method pulls the 'ev' and 'lhs' values out of
   * instantiations of implicit -`Ops` classes.
   *
   * For instance, given a tree like:
   *
   * {{{
   *   new FooOps(x)(ev)
   * }}}
   *
   * This method would return `(ev, x)`.
   *
   * @group macros
   */
  def unpack[T[_], A](c: Context) = {
    import c.universe._
    c.prefix.tree match {
      case Apply(Apply(TypeApply(_, _), List(x)), List(ev)) => (ev, x)
      case t => c.abort(c.enclosingPosition,
        "Cannot extract subject of operator (tree = %s)" format t)
    }
  }

  /**
   * Given context, this method pulls the 'lhs' value out of
   * instantiations of implicit -`Ops` classes.
   *
   * For instance, given a tree like:
   *
   * {{{
   *   new FooOps(x)
   * }}}
   *
   * This method would return `x`.
   *
   * @group macros
   */
  def unpackWithoutEv(c: Context) = {
    import c.universe._
    c.prefix.tree match {
      case Apply(TypeApply(_, _), List(lhs)) => lhs
      case t => c.abort(c.enclosingPosition,
        "Cannot extract subject of operator (tree = %s)" format t)
    }
  }

  /**
   * Provide a canonical mapping between "operator names" used in `Ops`
   * classes and the actual method names used for type classes.
   *
   * It's worth noting that a particular instance of `Ops` must always
   * map a given symbol a single method name. If you want to be able
   * to map the same symbol to different names in different contexts,
   * you'll need to create multiple `Ops` instances and configure them
   * appropriately.
   *
   * In general "textual" method names should just pass through to the
   * typeclass--it is probably not wise to provide mappings for them
   * here.
   */
  def findMethodName(c: Context) = {
    import c.universe._
    val s = c.macroApplication.symbol.name.toString
    TermName(operatorNames.getOrElse(s, s))
  }

  /**
   * Map of symbolic -> textual name conversions.
   *
   * If this map is empty, the macros will not do any special
   * rewriting and all names will be passed through.
   *
   * Symbolic names should be written as Scala would represent them
   * internally. For example, `+` should be written as `\$plus`.
   */
  def operatorNames: Map[String, String]
}

trait DefaultOperatorNames {

  val operatorNames = Map(
    // Eq (=== =!=)
    ("$eq$eq$eq", "eqv"),
    ("$eq$bang$eq", "neqv"),

    // PartialOrder (> >= < <=)
    ("$greater", "gt"),
    ("$greater$eq", "gteqv"),
    ("$less", "lt"),
    ("$less$eq", "lteqv"),

    // Semigroup (|+| |-|)
    ("$bar$plus$bar", "combine"),
    ("$bar$minus$bar", "remove"),

    // Ring (unary_- + - * **)
    ("unary_$minus", "negate"),
    ("$plus", "plus"),
    ("$minus", "minus"),
    ("$times", "times"),
    ("$times$times", "pow"),

    // EuclideanRing (/~ % /%)
    ("$div$tilde", "quot"),
    ("$percent", "mod"),
    ("$div$percent", "quotmod"),

    // Field (/)
    ("$div", "div"),

    // BooleanAlgebra (^ | & ~)
    ("$up", "xor"),
    ("$bar", "or"),
    ("$amp", "and"),
    ("unary_$tilde", "complement"),

    // BitString (<< >> >>>)
    ("$less$less", "leftShift"),
    ("$greater$greater$greater", "rightShift"),
    ("$greater$greater", "signedRightShift"),

    // VectorSpace (*: :* :/ ⋅)
    ("$times$colon", "timesl"),
    ("$colon$times", "timesr"),
    ("$colon$div", "divr"),
    ("$u22C5", "dot"),

    // GroupAction (|+|> <|+| +> <+ *> <*)
    ("$bar$plus$bar$greater", "actl"),
    ("$less$bar$plus$bar", "actr"),
    ("$plus$greater", "gplusl"),
    ("$less$plus", "gplusr"),
    ("$times$greater", "gtimesl"),
    ("$less$times", "gtimesr"),

    // Torsor (<|-|> <-> )
    ("$less$bar$minus$bar$greater", "pdiff"),
    ("$less$minus$greater", "pminus"),
    ("$less$div$greater", "pdiv")
  )
}

object DefaultOps extends Ops with DefaultOperatorNames




© 2015 - 2025 Weber Informatics LLC | Privacy Policy