scala - Using Macro to Make Case Class -


given following macro (thanks @travisbrown help ):

jetdim.scala

case class jetdim(dimension: int) {   require(dimension > 0) }  object jetdim {   def validate(dimension: int): int = macro jetdimmacro.apply   def build(dimension: int): jetdim = jetdim(validate(dimension)) } 

jetdimmacro.scala

import reflect.macros.context  object jetdimmacro {      sealed trait posintcheckresult     case class lteqzero(x: int) extends posintcheckresult     case object notconstant extends posintcheckresult      def apply(c: context)(dimension: c.expr[int]): c.expr[int] = {          import c.universe._          getint(c)(dimension) match {             case right(_)          => reify { dimension.splice }             case left(lteqzero(x)) => c.abort(c.enclosingposition, s"$x must > 0.")             case left(notconstant) => reify { dimension.splice }         }     }      def getint(c: context)(dimension: c.expr[int]): either[posintcheckresult, int] = {          import c.universe._          dimension.tree match {             case literal(constant(x: int)) => if (x > 0) right(x) else left(lteqzero(x))             case _                         => left(notconstant)         }     } } 

it works repl:

scala> import spire.math.jetdim import spire.math.jetdim  scala> jetdim.validate(-55) <console>:9: error: -55 must > 0.               jetdim.validate(-55)                              ^  scala> jetdim.validate(100) res1: int = 100 

but, i'd build compile-time check (via jetdimmacro) case class's apply method.

attempt 1

case class jetdim(dimension: int) {   require(dimension > 0) }  object jetdim {   private def validate(dimension: int): int = macro jetdimmacro.apply   def build(dimension: int): jetdim = jetdim(validate(dimension)) } 

but failed:

scala> import spire.math.jetdim import spire.math.jetdim  scala> jetdim.build(-55) java.lang.illegalargumentexception: requirement failed   @ scala.predef$.require(predef.scala:207)   @ spire.math.jetdim.<init>(jet.scala:21)   @ spire.math.jetdim$.build(jet.scala:26)   ... 43 elided 

attempt 2

class jetdim(dim: int) {   require(dim > 0)    def dimension: int = dim }  object jetdim {   private def validate(dimension: int): int = macro jetdimmacro.apply   def apply(dimension: int): jetdim = {     validate(dimension)     new jetdim(dimension)   } } 

yet failed too:

scala> import spire.math.jetdim import spire.math.jetdim  scala> jetdim(555) res0: spire.math.jetdim = spire.math.jetdim@4b56f205  scala> jetdim(-555) java.lang.illegalargumentexception: requirement failed   @ scala.predef$.require(predef.scala:207)   @ spire.math.jetdim.<init>(jet.scala:21)   @ spire.math.jetdim$.apply(jet.scala:30)   ... 43 elided 

i thought modify jetdimmacro#apply return jetdim rather int. however, jetdim lives in core project, which, see, depends on macros project (where jetdimmacro lives).

how can use validate method jetdim's companion object check positive int's @ compile-time?

the problem time call validate in apply no longer dealing constant (singleton type). so, validate gets non-constant int.

as alternative, try using implicit witness positive ints, jetdim takes constructor. instance, like:

package com.example  case class jetdim(n: positiveint)  case class positiveint(value: int) {   require(value > 0) } 

then, add implicit (macro) conversion int => positiveint check.

import scala.language.experimental.macros  import scala.reflect.macros.blackbox.context  object positiveint {   implicit def wrapconstantint(n: int): positiveint = macro verifypositiveint    def verifypositiveint(c: context)(n: c.expr[int]): c.expr[positiveint] = {     import c.universe._      val tree = n.tree match {       case literal(constant(x: int)) if x > 0 =>         q"_root_.com.example.positiveint($n)"       case literal(constant(x: int)) =>         c.abort(c.enclosingposition, s"$x <= 0")       case x =>         c.abort(c.enclosingposition, s"cannot verify $x > 0")     }     c.expr(tree)   } } 

you can use jetdim(12), pass, or jetdim(-12), fail (the macro expands int positiveint).


Comments

Popular posts from this blog

php - failed to open stream: HTTP request failed! HTTP/1.0 400 Bad Request -

java - How to filter a backspace keyboard input -

java - Show Soft Keyboard when EditText Appears -