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.
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.