2013-06-17 14 views
5

Wydaje się niemożliwe do wdrożenia metody abstrakcyjne poprzez def makr:Wdrożenie metody abstrakcyjne z 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 
} 

ten nie powiedzie się z powodu następującego błędu:

[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]  ^

jeśli usunąć extends A go kompiluje. Ale oczywiście chcę AImpl, aby zadowolić cechę A. Jak to naprawić?


Kolejna próba:

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

nadaje nowy błąd:

[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

Czy rzeczywiście potrzebujesz wywołania '(a: AImpl) .foo()', aby być makrem i zostać rozwiniętym w miejscu użycia lub czy potrzebujesz tylko wygenerować zawartość (normalna) metoda 'foo()'? – gourlaysama

Odpowiedz

4

Na pewno nie testy z makro skompilowany pierwszy i AImpl później?

Stosując metodę przesyłania dalej jak swoim drugim razem wydaje się działać (z 2.10.2):

// 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, tak, doszedłem do wniosku, że potrzebuję dwóch biegów kompilacji. Myślę również, że rozumiem, dlaczego nie mogę "zastosować" metody abstrakcyjnej z makrem - ponieważ makroinwazja "zamienia" metodę na treść makra. To znaczy, metoda 'bar()' w twoim kodzie zniknie, więc 'def foo(): Jednostka = {}' pozostaje. Więc tak, mogę iść do tego forwardera, lub używam kompozycji (implementuję całą cechę z wewnątrz makra). –

4

Nie jestem pewien, czy to jest prawidłowe, więc proszę dodać dodatkową odpowiedź autorytatywny, proszę.

Zaczynam dopiero rozumieć, jak działają makra def. Błędne założenie w pytaniu jest takie, że def bar(): Unit = macro ... faktycznie tworzy środowisko wykonawcze barmetoda. Zamiast tego tworzy ... no cóż, makro macro, więc każde wywołanie tego makra powoduje splajtowanie w wyrażeniu.

Widzę więc dwie rzeczy. Albo typ powrotu staje się c.Expr[DefDef], ale nie jestem pewien, czy to nawet możliwe, i prawdopodobnie jest to o wiele więcej pracy. Drugą opcją jest wygenerowanie całej cechy, np. jako anonimowego Klasa:

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) 
    } 
} 

wówczas zamiast wstawek, trzeba skład:

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

    bar.foo() 
} 

Dużą ssać jest, muszę założyć projekt sub z SBT bo inaczej te pliki nie kompilować w tym samym czasie: -/

+1

oh, teraz widzę, co próbujesz zrobić. Makra są rzeczywiście rozszerzane i wstawiane * na stronie wywołania *, więc nie mogą implementować ani zastępować abstrakcyjnej metody, ponieważ w rzeczywistości nie tworzą metody. – gourlaysama

+0

To prawda. dzięki za odpowiedź! –

+0

'Muszę skonfigurować sub projektu z sbt, ponieważ inaczej te pliki nie kompilują się w tym samym czasie' Czy to z powodu: 'najczęstszą przyczyną jest to, że nie można używać makr w tym samym uruchomieniu kompilacji który je definiuje? –