2013-06-17 10 views
5

Es scheint unmöglich zu sein, abstrakte Methoden über def Makros zu implementieren:Implementieren abstrakte Methoden mit einem def Makro

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

trait A { 
    def foo(): Unit 
} 

object AImpl { 
    def fooImpl(c: Context)(): c.Expr[Unit] = { 
    import c.universe._ 
    c.Expr[Unit](reify().tree) 
    } 
} 
trait AImpl extends A { 
    def foo(): Unit = macro AImpl.fooImpl 
} 

Dies mit dem folgenden Fehler fehl:

[error] .../A.scala:17: overriding method foo in trait A of type()Unit; 
[error] macro method foo cannot override an abstract method 
[error] def foo(): Unit = macro AImpl.fooImpl 
[error]  ^

Wenn ich die extends A es entfernen kompiliert. Aber offensichtlich möchte ich AImpl erfüllen Merkmal A. Wie behebe ich das?


Ein weiterer Versuch:

trait AImpl extends A { 
    def foo(): Unit = bar() 
    def bar(): Unit = macro AImpl.fooImpl 
} 

Gibt neue Fehler:

[error] macro implementation not found: bar (the most common reason for that is that 
    you cannot use macro implementations in the same compilation run that defines them) 
[error] one error found 
+0

Benötigen Sie tatsächlich einen Aufruf an '(a: AImpl) .foo()', um ein Makro zu sein und auf der Website zu erweitern, oder müssen Sie nur den Inhalt von generieren die (normale) 'foo()' Methode? – gourlaysama

Antwort

4

Sind Sie sicher, dass Sie mit dem Makro prüften zuerst kompilierten und AImpl später?

einen Spediteur Methode, wie Ihr zweiter Versuch verwenden scheint (mit 2.10.2) zu arbeiten:

// first compilation run 

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

trait A { 
    def foo(): Unit 
} 

object AImplMacros { 
    def fooImpl(c: Context)(): c.Expr[Unit] = { 
    import c.universe._ 
    c.Expr[Unit](reify().tree) 
    } 
} 

// second compilation run 

trait AImpl extends A { 
    def foo(): Unit = bar() 
    def bar(): Unit = macro AImplMacros.fooImpl 
} 

// compiles and runs: 

scala> val a = new AnyRef with AImpl 
a: AImpl = [email protected] 

scala> a.foo 

scala> a.bar 
+0

Ok, ja, ich habe herausgefunden, dass ich zwei Kompilationsläufe benötige. Ich denke auch, ich verstehe, warum ich eine abstrakte Methode nicht mit einem Makro "implementieren" kann, weil der Makroaufruf die Methode durch den Makrokörper "ersetzt". Das heißt, die Methode 'bar()' in Ihrem Code ist verschwunden, so dass 'def foo(): Unit = {}' übrig bleibt. Also ja, ich kann für diese Weiterleitung gehen, oder ich verwende Zusammensetzung (implementieren Sie die ganze Eigenschaft aus dem Makro). –

4

Ich bin nicht sicher, ob dies richtig ist, so bitte eine zusätzliche verbindliche Antwort hinzuzufügen, bitte.

Ich beginne erst zu verstehen, wie def Makros funktionieren. Die falsche Annahme in der Frage ist, dass def bar(): Unit = macro ... tatsächlich eine Laufzeit barMethode erstellt. Stattdessen erstellt es ... nun, ein Makro, so dass jeder Aufruf dieses Makros nur im Ausdruck spleißt.

So sehe ich zwei Dinge. Entweder wird der Rückgabetyp c.Expr[DefDef], aber ich bin mir nicht sicher, ob das überhaupt möglich ist, und wahrscheinlich ist es viel mehr Arbeit. Die zweite Option besteht darin, stattdessen das gesamte Merkmal zu erzeugen, z. als eine anonyme Klasse:

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

trait A { 
    def foo(): Unit 
} 

object AImpl { 
    def body: A = macro bodyImpl 
    def bodyImpl(c: Context): c.Expr[A] = { 
    import c.universe._ 
    val r = reify { new A { def foo() { println("schoko")}}} 
    c.Expr[A](r.tree) 
    } 
} 

Dann statt mixin, haben Sie Zusammensetzung:

object AHolder extends App { 
    val bar: A = AImpl.body 

    bar.foo() 
} 

Der große saugen ist, ich brauche ein Teilprojekt mit SBT einzurichten, weil sonst diese Dateien nicht tun kompilieren zur gleichen Zeit: -/

+1

Oh, ich sehe jetzt, was Sie versucht haben zu tun. Makros sind in der Tat an der Aufrufstelle * erweitert und inline *, so dass sie eine abstrakte Methode nicht implementieren oder überschreiben können, da sie keine Methode tatsächlich erzeugen. – gourlaysama

+0

das ist richtig. danke für die Antwort! –

+0

'Ich muss ein Unterprojekt mit sbt einrichten, da diese Dateien sonst nicht zur selben Zeit kompiliert werden.} Das liegt daran:' Der häufigste Grund dafür ist, dass Sie Makroimplementierungen nicht im selben Kompilierungslauf verwenden können das definiert sie? –

Verwandte Themen