2012-06-14 12 views
8

मैं एक डीएसएल विकसित कर रहा हूं और मैक्रो का विस्तार करते समय मुझे "फ्री टर्म" विफलता मिल रही है। मैं जानना चाहता हूं कि इसे टाला जा सकता है या नहीं। मैंने निम्नलिखित स्थिति में समस्या को सरल बना दिया है।क्या यह फ्री-टर्म-वेरिएबल त्रुटि (मैक्रो विस्तार पर उत्पादित) से बचा जा सकता है?

val list = join { 
    0 
    1 
    2 
    3 
} 
println(list) 

जहां में शामिल होने के लिए मैक्रो जिसका कार्यान्वयन है:

मान लीजिए हम इस अभिव्यक्ति है

def join(c: Ctx)(a: c.Expr[Int]): c.Expr[List[Int]] = { 
    import c.mirror._ 
    a.tree match { 
    case Block(list, ret) => 
     // c.reify(List(new c.Expr(list(0)).eval, 
     //    new c.Expr(list(1)).eval, 
     //    new c.Expr(list(2)).eval) :+ new c.Expr(ret).eval) 
     c.reify((for (expr <- list) yield new c.Expr(expr).eval) :+ new c.Expr(ret).eval) 
    } 
} 

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

"मैक्रो विस्तार में Macros.scala: 48: 18 में शामिल होने से परिभाषित मुक्त शब्द परिवर्तनीय सूची शामिल है। क्या आप eval का उपयोग करना भूल गए हैं स्प्लिसिंग एक reifee में इस चर? तुम मुक्त अवधि चर ट्रैकिंग मुसीबतों है, तो -Xlog मुक्त-शब्द "

इस प्राप्त किए बिना के लिए-समझ (या पुनरावर्तक या जो कुछ भी) लागू करने के लिए कोई तरीका है उपयोग करने पर विचार त्रुटि? वैसे, मैं 2.10-एम 3 का उपयोग कर रहा हूँ।

उत्तर

15

समस्या यह है कि आपका कोड संकलन-समय और रनटाइम अवधारणाओं को मिश्रित करता है।

आप जिस "सूची" चर का उपयोग कर रहे हैं वह एक संकलन-समय मान है (यानी इसे संकलन-समय के दौरान पुनरावृत्त किया जाना चाहिए), और आप रनटाइम तक इसे बनाए रखने के लिए पुन: पुष्टि कर रहे हैं (व्युत्पन्न विभाजन द्वारा मान)। यह क्रॉस-स्टेज कन्डर्रम एक तथाकथित मुक्त अवधि के निर्माण की ओर जाता है।

संक्षेप में, नि: शुल्क शर्तें स्टब्स हैं जो पिछले चरणों से मूल्यों का संदर्भ देती हैं। उदाहरण के लिए, निम्नलिखित स्निपेट:

val x = 2 
reify(x) 

के रूप में संकलित किया जा चाहेंगे इस प्रकार है:

val free$x1 = newFreeTerm("x", staticClass("scala.Int").asTypeConstructor, x); 
Ident(free$x1) 

चालाक, है ना? परिणाम इस तथ्य को बरकरार रखता है कि एक्स एक पहचान है, इसके प्रकार (संकलन-समय विशेषताओं) को संरक्षित करता है, लेकिन फिर भी, इसके मूल्य को भी संदर्भित करता है (एक रन-टाइम विशेषता)। यह लेक्सिकल स्कोपिंग द्वारा संभव बनाया गया है।

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

और भी बदतर है। यदि उपरोक्त स्निपेट मैक्रो के अंदर लिखा गया है, तो एक्स केवल संकलन-समय के दौरान मौजूद है, यानी जेवीएम में जो कंपाइलर चलाता है। लेकिन जब संकलक समाप्त हो जाता है, तो यह चला गया है।

हालांकि, एक्स के संदर्भ वाले मैक्रो विस्तार के परिणाम रनटाइम पर चलने वाले हैं (अधिकतर, एक अलग जेवीएम में)। इस बात को समझने के लिए, आपको क्रॉस-स्टेज दृढ़ता की आवश्यकता होगी, यानी किसी भी तरह मनमाने ढंग से संकलित-समय मानों को क्रमबद्ध करने और रनटाइम के दौरान उन्हें deserialize करने की क्षमता। मुझे नहीं पता कि स्कैला जैसी संकलित भाषा में इसे कैसे किया जाए।


ध्यान दें कि कुछ मामलों में पार-चरण दृढ़ता संभव है।उदाहरण के लिए, अगर एक्स एक स्थिर वस्तु का एक क्षेत्र था:

object Foo { val x = 2 } 
import Foo._ 
reify(x) 

तो यह एक नि: शुल्क शब्द के रूप में अंत नहीं होगा, लेकिन एक सीधा फैशन में reified किया जाएगा:

Select(Ident(staticModule("Foo")), newTermName("x")) 

यह वह जगह है स्काला डेज़ 2012: http://skillsmatter.com/podcast/scala/haskell-cloud पर एसपीजे की बातचीत में एक दिलचस्प अवधारणा पर भी चर्चा की गई।

यह सत्यापित करने के लिए कि कुछ अभिव्यक्ति में मुक्त शब्द नहीं हैं, हास्केल में वे कंपाइलर, Static प्रकार कन्स्ट्रक्टर के लिए एक नया अंतर्निहित आदिम जोड़ते हैं। मैक्रोज़ के साथ, हम इसे स्वाभाविक रूप से पुन: उपयोग करके कर सकते हैं (जो स्वयं ही एक मैक्रो है)। यहां चर्चा देखें: https://groups.google.com/forum/#!topic/scala-internals/-42PWNkQJNA


ठीक है अब हमने देखा है कि मूल कोड के साथ वास्तव में समस्या क्या है, तो हम इसे कैसे काम करते हैं?

दुर्भाग्यवश हमें मैन्युअल एएसटी निर्माण में वापस आना होगा, क्योंकि सुधार को गतिशील पेड़ों को व्यक्त करने में कठिन समय होता है। मैक्रोलॉजी में सुधार के लिए आदर्श उपयोग केस मैक्रो संकलन समय में ज्ञात छेद के प्रकारों के साथ एक स्थिर टेम्पलेट है। एक कदम एक तरफ ले लो - और आपको हाथों से पेड़ों के निर्माण का सहारा लेना होगा।

नीचे लाइन के साथ जाना है कि आप है निम्नलिखित (काम करता है हाल ही में जारी साथ 2.10.0-एम 4, वास्तव में क्या बदल गया है देखने के लिए स्केला भाषा में माइग्रेशन मार्गदर्शिका देखें: http://groups.google.com/group/scala-language/browse_thread/thread/bf079865ad42249c):

import scala.reflect.makro.Context 

object Macros { 
    def join_impl(c: Context)(a: c.Expr[Int]): c.Expr[List[Int]] = { 
    import c.universe._ 
    import definitions._ 
    a.tree match { 
     case Block(list, ret) => 
     c.Expr((list :+ ret).foldRight(Ident(NilModule): Tree)((el, acc) => 
      Apply(Select(acc, newTermName("$colon$colon")), List(el)))) 
    } 
    } 

    def join(a: Int): List[Int] = macro join_impl 
} 
+0

स्कैला 2.11 में कुछ आसान होगा? क्या अर्ध-उद्धरण मदद करने जा रहे हैं? (मैंने गहरे में मैक्रोज़ का अध्ययन नहीं किया है, लेकिन जब भी मैं कोशिश करता हूं तो मैं इसमें बंपिंग करता हूं) – HRJ

+0

क्वेशिकोट्स थोड़ी मदद करने जा रहे हैं: https://gist.github.com/densh/6209261 –

संबंधित मुद्दे