Re: Writing a Macro to Test if Value Equals to 5
Kevin Meredith <[email protected]>
| Newsgroups | gmane.comp.lang.scala |
|---|---|
| Message-ID | <[email protected]> |
Actually, I tried to use a `safe constructor` approach, i.e. make the case
class's constructor to be private & add a factory method.
But that approach did not work.
*Equals5.scala*
package net
import scala.language.experimental.macros
import scala.reflect.macros.Context
case class Equals5 private (value: Int) {
require(value == 5)
}
object Equals5 {
def build(n: Int): Equals5 = macro verifyIntEquals5
def verifyIntEquals5(c: Context)(n: c.Expr[Int]): c.Expr[Equals5] = {
import c.universe._
val tree = n.tree match {
case Literal(Constant(x: Int)) if x == 5 =>
q"_root_.net.Equals5($n)"
case Literal(Constant(x: Int)) =>
c.abort(c.enclosingPosition, s"$x != 0")
case _ =>
q"_root_.net.Equals5($n)"
}
c.Expr(tree)
}
}
*Testing:*
scala> Equals5.build(1000)
<console>:12: error: 1000 != 0
Equals5.build(1000)
^
scala> Equals5.build(5)
<console>:12: error: constructor Equals5 in class Equals5 cannot be
accessed in object $iw
Equals5.build(5)
^
On Saturday, September 12, 2015 at 10:51:25 PM UTC-4, Kevin Meredith wrote:
>
> Given the following macro:
>
> package net
>
> import scala.language.experimental.macros
> import scala.reflect.macros.Context
>
> case class Equals5(value: Int) {
> require(value == 5)
> }
>
> object Equals5 {
> implicit def wrapInt(n: Int): Equals5 = macro verifyIntEquals5
>
> def verifyIntEquals5(c: Context)(n: c.Expr[Int]): c.Expr[Equals5] = {
> import c.universe._
>
> val tree = n.tree match {
> case Literal(Constant(x: Int)) if x == 5 =>
> q"_root_.net.Equals5($n)"
> case Literal(Constant(x: Int)) =>
> c.abort(c.enclosingPosition, s"$x != 0")
> case _ =>
> q"_root_.net.Equals5($n)"
> }
> c.Expr(tree)
> }
> }
>
> I wrote the following tests using scalatest:
>
> package net
>
> import org.scalatest.Matchers
>
> import org.scalatest._
> import org.scalatest.prop.Checkers._
>
> class Equals5Test extends FlatSpec with Matchers {
>
> it should "throw an IllegalArgumentException when trying to make an
> invalid `Equals5` directly" in {
> intercept[IllegalArgumentException] {
> Equals5(-555) //
> }
> }
>
> it should "not compile when using the macro constructor to build an
> `Equals5` with a constant" in {
> """
> import net.Equals5._; wrapInt(-555)
> """.shouldNot(compile)
> }
> }
>
> Both tests succeed.
>
> But, I'd like for `Equals5.apply` to use the `Equals5.wrapInt` method - to
> make use of the macro.
>
> I believe that I could do this by making Equals5#apply to be private, and
> then require a factory method (that uses the macro)
> to be used in order to create an `Equals5` class.
>
> Is there a better alternative? Can the `implicit def wrapInt` be used? Or
> do implicits not get used during the macro compile-time stage?
>
> Thanks,
> Kevin
>
--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [email protected].
For more options, visit https://groups.google.com/d/optout.