2015-10-22 19 views
7

Scala 2.11+ uygulamasında ek açıklama makroları kullanarak yöntemlerin takma adlarını oluşturmak istiyorum. Hatta bunun mümkün olduğundan bile emin değilim. Evet ise nasıl?Yöntemler oluşturmak için Scala makrolarını kullanın

class Socket { 
    def load(n: Int): Seq[Byte] = // .... 
    def !(n: Int) = load(n) 
    def ask(n: Int) = load(n) 
    def read(n: Int) = load(n) 
} 

yukarıdaki elbette edilir: -

Örnek bu aşağıda göz önüne alındığında, açıklama makro ben şöyle eşanlamlı yöntem koçanları üretmek için yukarıdaki istiyoruz

class Socket { 
    @alias(aliases = Seq("!", "ask", "read")) 
    def load(n: Int): Seq[Byte] = {/* impl */} 
} 

içine genişletmek istiyorum Bu örnek, API'lerin sync/async sürümlerini veya çok sayıda eşanlamlı DSL'leri otomatik olarak oluşturmak için faydalı olan bu tekniği görebiliyorum. Bu üretilen yöntemleri Scaladoc'ta da açığa çıkarmak mümkün mü? Scala meta kullanarak bu mümkün mü?

Not: Ne Soruyorum oldukça farklıdır: https://github.com/ktoso/scala-macro-method-alias

soru biraz farklıdır ve bir çok geçmişte Scala makro arazi değişti olarak da this tekrarı olarak işaretlemek etmeyiniz 3 yıl. belirtildiği gibi

+0

. Makrolarla ilgili yeni bir buluşmadan bu slayt destesine göz atın: https://speakerdeck.com/bwmcadams/ny-scala-meetup-scala-macros-or-how-i-learned-to-stop-worrying-and-mumble- wtf – JoseM

cevap

8

Bu tam mümkün görünmüyor. Bir sınıf üyesi üzerinde bir makro notu kullanmak, sınıfın ağacının kendisinde işlem yapmanıza izin vermez. Yani bir makro ek açıklamanın bulunduğu bir sınıf içinde bir yöntem açıklama yaparken, macroTransform(annottees: Any*) adı verilecek, fakat sadece annottee yöntemi kendisi olacaktır.

İki ek açıklama ile çalışan bir ispat kanıtı elde edebildim. Belli ki, dersi sadece ders notu kadar güzel değil, ama etrafta başka bir şey düşünemiyorum.

Sen gerekir: ebeveyn sınıfının bir makro açıklama genişletmek istediğiniz hangi yöntemleri bulmak mümkün olacak şekilde

fikir
import scala.annotation.{ StaticAnnotation, compileTimeOnly } 
import scala.language.experimental.macros 
import scala.reflect.macros.whitebox.Context 

, sen bu açıklama ile her yöntemi açıklamalar oluşturabilirsiniz.

class alias(aliases: String *) extends StaticAnnotation 

Ardından makro:

// Annotate the containing class to expand aliased methods within 
@compileTimeOnly("You must enable the macro paradise plugin.") 
class aliased extends StaticAnnotation { 
    def macroTransform(annottees: Any*): Any = macro AliasMacroImpl.impl 
} 

object AliasMacroImpl { 

    def impl(c: Context)(annottees: c.Expr[Any]*): c.Expr[Any] = { 
    import c.universe._ 

    val result = annottees map (_.tree) match { 
     // Match a class, and expand. 
     case (classDef @ q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }") :: _ => 

     val aliasedDefs = for { 
      q"@alias(..$aliases) def $tname[..$tparams](...$paramss): $tpt = $expr" <- stats 
      Literal(Constant(alias)) <- aliases 
      ident = TermName(alias.toString) 
     } yield { 
      val args = paramss map { paramList => 
      paramList.map { case q"$_ val $param: $_ = $_" => q"$param" } 
      } 

      q"def $ident[..$tparams](...$paramss): $tpt = $tname(...$args)" 
     } 

     if(aliasedDefs.nonEmpty) { 
      q""" 
      $mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => 
       ..$stats 
       ..$aliasedDefs 
      } 
      """ 
     } else classDef 
     // Not a class. 
     case _ => c.abort(c.enclosingPosition, "Invalid annotation target: not a class") 
    } 

    c.Expr[Any](result) 
    } 

} 

bu uygulama kırılgan olacaktır unutmayın. Sadece, ilkinin ClassDef olduğunu kontrol etmek için annotları denetler. Daha sonra, sınıfın üyelerine @alias ile açıklamalı yöntemler ekler ve sınıfa yeniden eklenecek çok sayıda takma ağaç oluşturur. Açıklamalı herhangi bir yöntem yoksa, orijinal sınıf ağacını döndürür. Bu, yinelenen yöntem adlarını algılamayacak ve değiştiricileri yok edecektir (derleyici, ek açıklamaları ve değiştiricileri aynı anda eşleştirmeme izin vermeyecektir).

Bu

kolayca arkadaşı yanı nesneleri işlemek için genişletilmiş, ancak kod daha küçük tutmak için onları dışarı bırakılabilir. Kullandığım eşleştiriciler için quasiquotes syntax summary'a bakın. Taşıma arkadaşı nesneler case classDef :: objDef :: Nil işlemek için result maçı ve dava objDef :: Nil değiştirerek gerektirecektir.Kullanımda

:

@aliased 
class Socket { 
    @alias("ask", "read") 
    def load(n: Int): Seq[Byte] = Seq(1, 2, 3).map(_.toByte) 
} 

scala> val socket = new Socket 
socket: Socket = [email protected] 

scala> socket.load(5) 
res0: Seq[Byte] = List(1, 2, 3) 

scala> socket.ask(5) 
res1: Seq[Byte] = List(1, 2, 3) 

scala> socket.read(5) 
res2: Seq[Byte] = List(1, 2, 3) 

Ayrıca işleyebilir birden parametre listeleri:

quasiquotes kullanılarak yapılabilir görünüyor
@aliased 
class Foo { 
    @alias("bar", "baz") 
    def test(a: Int, b: Int)(c: String) = a + b + c 
} 

scala> val foo = new Foo 
foo: Foo = [email protected] 

scala> foo.baz(1, 2)("4") 
res0: String = 34 
+0

Bazı nedenlerle '!' yöntemi kopyalandı, ancak işe yaramayacaktı. Şüpheliyim, çünkü bu özel bir karakter ve bir tür özel işleme ihtiyaç duyuyor, ama tam olarak nedenini bulmak için zamanım yok. –

İlgili konular