2013-01-17 21 views
180

से अज्ञात वर्ग के तरीकों के साथ एक संरचनात्मक प्रकार प्राप्त करना मान लीजिए कि हम एक मैक्रो लिखना चाहते हैं जो किसी प्रकार के सदस्यों या विधियों के साथ किसी अज्ञात वर्ग को परिभाषित करता है, और उसके बाद उस वर्ग का एक उदाहरण बनाता है जो स्थिर रूप से संरचनात्मक प्रकार के रूप में टाइप किया गया है उन तरीकों, आदि यह 2.10.0 में मैक्रो प्रणाली के साथ संभव है, और प्रकार सदस्य हिस्सा बहुत आसान है: (। कहाँ ReflectionUtils एक convenience trait कि मेरे constructor तरीका प्रदान करता है है)मैक्रो

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

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

scala> MacroExample.foo("T") 
res0: AnyRef{type T = Int} = [email protected] 

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

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 = [email protected] 

लेकिन हम वहाँ में एक अतिरिक्त गुमनाम वर्ग छड़ी अगर:

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} = [email protected] 

scala> res0.test 
res1: Int = 42 

यह बहुत आसान-यह आप this जैसी चीज़ें कर के लिए देता है उदाहरण-लेकिन मुझे समझ नहीं आता क्यों यह काम करता है, और प्रकार सदस्य संस्करण में काम करता है, लेकिन नहीं bar। मुझे यह may not be defined behavior पता है, लेकिन क्या यह कोई समझ में आता है? एक मैक्रो से संरचनात्मक प्रकार (इसके तरीकों के साथ) पाने के लिए एक क्लीनर तरीका है?

+14

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

+1

ध्यान दें कि मैंने एक मुद्दा दायर किया है [यहां] (https://issues.scala-lang.org/browse/SI-6992)। –

+0

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

उत्तर

8

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

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

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

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

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

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

+0

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

+0

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