मैक्रो से अनाम वर्ग की विधियों के साथ एक संरचनात्मक प्रकार प्राप्त करना


181

मान लें कि हम एक मैक्रो लिखना चाहते हैं जो कुछ प्रकार के सदस्यों या विधियों के साथ एक अनाम वर्ग को परिभाषित करता है, और फिर उस वर्ग का एक उदाहरण बनाता है जो सांख्यिकीय रूप से उन विधियों के साथ एक संरचनात्मक प्रकार के रूप में टाइप किया जाता है, आदि। 2.10 में मैक्रो सिस्टम के साथ यह संभव है। 0, और प्रकार का सदस्य हिस्सा बेहद आसान है:

object MacroExample extends ReflectionUtils {
  import scala.language.experimental.macros
  import scala.reflect.macros.Context

  def foo(name: String): Any = macro foo_impl
  def foo_impl(c: Context)(name: c.Expr[String]) = {
    import c.universe._

    val Literal(Constant(lit: String)) = name.tree
    val anon = newTypeName(c.fresh)

    c.Expr(Block(
      ClassDef(
        Modifiers(Flag.FINAL), anon, Nil, Template(
          Nil, emptyValDef, List(
            constructor(c.universe),
            TypeDef(Modifiers(), newTypeName(lit), Nil, TypeTree(typeOf[Int]))
          )
        )
      ),
      Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
    ))
  }
}

(जहां ReflectionUtilsएक सुविधा विशेषता है जो मेरी constructorविधि प्रदान करती है ।)

यह मैक्रो हमें एक स्ट्रिंग शाब्दिक के रूप में अनाम वर्ग के प्रकार के सदस्य का नाम निर्दिष्ट करने देता है:

scala> MacroExample.foo("T")
res0: AnyRef{type T = Int} = $1$$1@7da533f6

ध्यान दें कि यह उचित रूप से टाइप किया गया है। हम पुष्टि कर सकते हैं कि सब कुछ उम्मीद के मुताबिक काम कर रहा है:

scala> implicitly[res0.T =:= Int]
res1: =:=[res0.T,Int] = <function1>

अब मान लीजिए कि हम एक विधि के साथ एक ही काम करने की कोशिश करते हैं:

def bar(name: String): Any = macro bar_impl
def bar_impl(c: Context)(name: c.Expr[String]) = {
  import c.universe._

  val Literal(Constant(lit: String)) = name.tree
  val anon = newTypeName(c.fresh)

  c.Expr(Block(
    ClassDef(
      Modifiers(Flag.FINAL), anon, Nil, Template(
        Nil, emptyValDef, List(
          constructor(c.universe),
          DefDef(
            Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
            c.literal(42).tree
          )
        )
      )
    ),
    Apply(Select(New(Ident(anon)), nme.CONSTRUCTOR), Nil)
  ))
}

लेकिन जब हम इसे आज़माते हैं, तो हमें एक संरचनात्मक प्रकार नहीं मिलता है:

scala> MacroExample.bar("test")
res1: AnyRef = $1$$1@da12492

लेकिन अगर हम एक अतिरिक्त अनाम वर्ग को वहाँ रखते हैं:

def baz(name: String): Any = macro baz_impl
def baz_impl(c: Context)(name: c.Expr[String]) = {
  import c.universe._

  val Literal(Constant(lit: String)) = name.tree
  val anon = newTypeName(c.fresh)
  val wrapper = newTypeName(c.fresh)

  c.Expr(Block(
    ClassDef(
      Modifiers(), anon, Nil, Template(
        Nil, emptyValDef, List(
          constructor(c.universe),
          DefDef(
            Modifiers(), newTermName(lit), Nil, Nil, TypeTree(),
            c.literal(42).tree
          )
        )
      )
    ),
    ClassDef(
      Modifiers(Flag.FINAL), wrapper, Nil,
      Template(Ident(anon) :: Nil, emptyValDef, constructor(c.universe) :: Nil)
    ),
    Apply(Select(New(Ident(wrapper)), nme.CONSTRUCTOR), Nil)
  ))
}

यह काम करता हैं:

scala> MacroExample.baz("test")
res0: AnyRef{def test: Int} = $2$$1@6663f834

scala> res0.test
res1: Int = 42

यह बेहद आसान है - यह आपको इस तरह की चीजें करने देता है , उदाहरण के लिए- लेकिन मुझे समझ में नहीं आता कि यह क्यों काम करता है, और प्रकार का सदस्य संस्करण काम करता है, लेकिन नहीं bar। मुझे पता है कि यह परिभाषित व्यवहार नहीं हो सकता है , लेकिन क्या इसका कोई मतलब है? क्या मैक्रो से संरचनात्मक प्रकार (उस पर विधियों के साथ) प्राप्त करने के लिए एक क्लीनर तरीका है?


14
दिलचस्प रूप से पर्याप्त है, अगर आप इसे मैक्रो में उत्पन्न करने के बजाय REPL में समान कोड लिखते हैं, तो यह काम करता है: scala> {अंतिम वर्ग anon {def x = 2}; new anon} res1: AnyRef {def x: Int} = anon $ 1 @ 5295c398। रिपोर्ट के लिए धन्यवाद! मैं इस सप्ताह देखूंगा।
यूजीन बर्माको

1
ध्यान दें कि मैंने यहां एक मुद्दा दर्ज किया है
ट्रैविस ब्राउन

नहीं, एक अवरोधक नहीं, धन्यवाद - अतिरिक्त अनाम वर्ग चाल ने मेरे लिए काम किया है जब भी मुझे इसकी आवश्यकता होती है। मैंने सिर्फ इस सवाल पर कुछ कहा, और स्थिति के बारे में उत्सुक था।
ट्रैविस ब्राउन

3
टाइप मेंबर पार्ट बेहद आसान है -> wTF? आप बेहद दरार हैं! बेशक अच्छे तरीके से :)
ZaoTaoBao

3
यहाँ 153 अपवोट हैं, और scala-lang.org पर इस मुद्दे के लिए केवल 1 है । अधिक upvotes वहाँ इसे तेजी से हल हो सकता है?
मूडबूम

जवाबों:


9

इस प्रश्न का उत्तर यहां ट्रैविस द्वारा डुप्लिकेट में दिया गया है । ट्रैकर और यूजीन की चर्चा (टिप्पणी और मेलिंग सूची में) में इस मुद्दे के लिंक हैं।

टाइप चेकर के प्रसिद्ध "स्काईला और चरीबडीस" खंड में, हमारा नायक तय करता है कि अंधेरे गुमनामी से बच जाएगा और प्रकाश को संरचनात्मक प्रकार के सदस्य के रूप में देखता है।

टाइप चेकर को चकमा देने के कुछ तरीके हैं (जो ओडिसीस को भेड़ को गले लगाने के लिए मजबूर नहीं करते हैं)। सबसे सरल एक डमी स्टेटमेंट सम्मिलित करना है ताकि ब्लॉक एक अनाम वर्ग की तरह न दिखे, इसके बाद उसका तात्कालिकता।

यदि टाइपर नोटिस करता है कि आप एक सार्वजनिक शब्द हैं जिसे बाहर से संदर्भित नहीं किया गया है, तो यह आपको निजी बना देगा।

object Mac {
  import scala.language.experimental.macros
  import scala.reflect.macros.Context

  /* Make an instance of a structural type with the named member. */
  def bar(name: String): Any = macro bar_impl

  def bar_impl(c: Context)(name: c.Expr[String]) = {
    import c.universe._
    val anon = TypeName(c.freshName)
    // next week, val q"${s: String}" = name.tree
    val Literal(Constant(s: String)) = name.tree
    val A    = TermName(s)
    val dmmy = TermName(c.freshName)
    val tree = q"""
      class $anon {
        def $A(i: Int): Int = 2 * i
      }
      val $dmmy = 0
      new $anon
    """
      // other ploys
      //(new $anon).asInstanceOf[{ def $A(i: Int): Int }]
      // reference the member
      //val res = new $anon
      //val $dmmy = res.$A _
      //res
      // the canonical ploy
      //new $anon { }  // braces required
    c.Expr(tree)
  }
}

2
मैं सिर्फ इस बात पर ध्यान दूंगा कि मैं वास्तव में इस प्रश्न में पहला समाधान प्रदान करता हूं (यह सिर्फ अन-क्सिक्वॉट है यहां)। मुझे यह उत्तर देते हुए खुशी हो रही है कि इस सवाल का जवाब है- मुझे लगता है कि मैं निश्चित रूप से बग के ठीक होने का इंतजार कर रहा हूं।
ट्रैविस ब्राउन

@ TravisBrown मुझे यकीन है कि आपके बैट बेल्ट में अन्य उपकरण भी हैं। सिर के लिए Thx: मैंने मान लिया कि आपके एएसटी "पुराने अतिरिक्त ब्रेसिज़ ट्रिक" थे, लेकिन अब मैं देखता हूं कि क्लासडिफ / अप्लाई अपने ही ब्लॉक में नहीं लिपटे हैं, जैसा कि होता है new $anon {}। मेरा अन्य टेक-ऑफ यह है कि भविष्य में मैं anonमैक्रो में क्वासिकोट्स या इसी तरह के विशेष नामों के साथ उपयोग नहीं करूंगा।
सोम-संवत

q "$ {s: स्ट्रिंग}" वाक्यविन्यास थोड़ा विलंबित हो जाता है, खासकर यदि आप स्वर्ग का उपयोग कर रहे हैं। तो अगले हफ्ते की बजाय अगले महीने की तरह।
डेनिस शबलिन

@ som-snytt @ denys-shabalin, क्या संरचनात्मक प्रकार ए-ला के लिए एक विशेष प्रकार की प्रवंचना है shapeless.Generic? Auxपैटर्न रिटर्न प्रकारों को बाध्य करने के मेरे सर्वोत्तम इरादों के बावजूद संकलक संरचनात्मक प्रकार के माध्यम से देखने से इनकार करता है।
फलावियन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.