2016-03-23 2 views
0

J'ai besoin de macros Scala (reify, quasiquote, macro impl) pour ma bibliothèque d'assertions Scala.Les macros Scala peuvent-elles être définies à l'intérieur d'une classe (en tant que méthodes de cette classe)?


Je veux être en mesure de le faire:

object1.assertEquals(object2) // success: object1 = object2 

Ou comme ceci:

3.assertEquals(1 + 1) // failure: 1 + 1 /= 3 

Peut-on définir des macros Scala dans une classe implicite?

+0

Oui, une macro définition est juste un appel de méthode. Le préfixe que vous voyez dans l'application sera différent. –

+0

@ som-snytt - que voulez-vous dire par "Le préfixe que vous voyez dans l'application aura l'air différent"? –

+0

@ som-snytt - si je veux que l'objet lui-même, "this", soit passé dans la macro, est-ce que je fais q "$ this" et l'utilise comme un arbre comme les autres arbres? –

Répondre

0

La mise en œuvre doit être dans un objet ou d'un faisceau macro, mais la méthode qui est mis en œuvre par la macro peut être dans une classe implicite. Notez que val self = q"${c.prefix}.self" est nécessaire pour obtenir la référence à l'objet qui est enveloppé par la classe implicite.

import scala.language.experimental.macros 
import scala.reflect.macros.blackbox.Context 


object assertions { 
    implicit class AssertEquals[T](val self: T) extends AnyVal { 
    def assertEquals(other: T): Unit = macro AssertionMacros.assertEquals[T] 
    } 

    object AssertionMacros { 
    def assertEquals[T](c: Context)(other: c.Tree): c.Tree = { 
     import c.universe._ 
     val self = q"${c.prefix}.self" 
     q"_root_.scala.Predef.assert($self == $other)" 
    } 
    } 
} 

Utilisation:

scala> import assertions._ 
import assertions._ 

scala> "foo" assertEquals "foo" 

scala> "foo" assertEquals "bar" 
java.lang.AssertionError: assertion failed 
    at scala.Predef$.assert(Predef.scala:151) 
    ... 43 elided 
+0

Ceci est une mise en œuvre beaucoup plus propre. J'aime particulièrement comment vous vous êtes référé explicitement à "soi" pour que la macro ait l'impression d'appartenir à la classe. Meilleure réponse. –

1

// ed: package écriture

package so 

trait AssertEquals[T, V] { 
    def assertEquals(t: T, v: V): Boolean 
} 

object AssertEquals { 
    implicit def assertEquals[T, V]: AssertEquals[T, V] = macro impl[T, V] 


    implicit class WithAssertEquals[T](t: T) { 
    def assertEquals[V](v: V)(implicit assertEquals: AssertEquals[T, V]): Boolean = assertEquals.assertEquals(t, v) 
    } 

    def impl[T: c.WeakTypeTag, V: c.WeakTypeTag](c: Context) = { 
    import c.universe._ 
    val _t = c.weakTypeOf[T] 
    val _v = c.weakTypeOf[V] 

    //edit 2 : use symbolOf instead typeOf 
    q""" 
     { 
     new ${symbolOf[so.AssertEquals[_, _]]}[${_t},${_v}]{ 
     def assertEquals(t: ${_t}, v: ${_v}): Boolean = t == v 
     } 
     } 
     """ 
    } 
} 

// test

import AssertEquals.WithAssertEquals 

assert(1.assertEquals(2) == false) 
assert(2.assertEquals(2) == true) 
assert("a".assertEquals("a") == true) 
assert("a".assertEquals("b") == false) 
assert("a".assertEquals(1) == false) 
+0

Voulez-vous dire "so" ou package? objet "so"? Parce que j'ai créé le paquet "so" et l'ai mis dedans et ça n'a pas marché. –

+0

pas trouvé: type AssertEquals [erreur] assert (1.assertEquals (2) == false) –

+0

utiliser 'package so' –