2009-12-20 19 views
8

आइए कहें कि मैं एफ # में किसी डोमेन-विशिष्ट भाषा के लिए एक पार्सर बना रहा हूं।एफ # डिजाइन पैटर्न

मैं भाव का प्रतिनिधित्व करने के लिए एक भेदभाव संघ द्वारा निर्धारित किए गए:

type Expression = 
     | Equality of Expression*Expression 
     | NonEquality of Expression*Expression 
     | Or of Expression*Expression 
     | And of Expression*Expression 
     | If of Expression*Expression 
     | IfElse of Expression*Expression*Expression 
     | Bool of bool 
     | Variable of string 
     | StringLiteral of string 

अब, मैं प्रकार Expression के एएसटी का निर्माण किया है और इसके लिए कोड उत्पन्न करने के लिए चाहते हैं। मेरे पास एक ऐसा फ़ंक्शन है जो एक अभिव्यक्ति पर टाइप अनुमान और प्रकार जांच करता है।

यह

let rec InferType expr = 
     match expr with 
     | Equality(e1,e2) -> CheckTypes (InferType e1) (InferType e2) 
     | Or(e1,e2) -> CheckTypes (InferType e1) (InferType e2) 
     | And(e1,e2) -> CheckTypes (InferType e1) (InferType e2) 
     ... 

तरह परिभाषित किया है और मैं एक और समारोह कोड जो एक समान पद्धति का अनुसरण करता उत्पन्न करने के लिए है: एक अभिव्यक्ति ले लो, संघ में प्रत्येक आइटम के लिए पैटर्न मिलान बयान लिखें।

मेरा प्रश्न है: क्या यह एफ # में ऐसा करने का बेवकूफ तरीका है?

ऐसा लगता है कि संघ के प्रत्येक सदस्य ने अपने InferType और GenerateCode को स्थानीय रूप से परिभाषित किया है तो यह क्लीनर होगा।

अगर मैं सी # उपयोग कर रहे थे, मैं InferType और GenerateCode के लिए आभासी तरीकों के साथ Expression बुलाया कुछ सार आधार वर्ग को परिभाषित करने और फिर उन्हें प्रत्येक उपवर्ग में ओवरराइड करेगी।

क्या ऐसा करने का कोई और तरीका है?

उत्तर

17

मुझे ऐसा लगता है कि यह करता है, तो संघ के प्रत्येक सदस्य को इसके साथ InferType और GenerateCode स्थानीय स्तर पर अपने आप ही परिभाषित क्लीनर होगा।

मेरा मानना ​​है कि आपका मतलब "अधिक परिचित" है, न कि "क्लीनर"।

वास्तव में, क्या आप आदर्श जनरेटर कार्यान्वयन 10 अलग-अलग वर्गों में फैले हुए हैं?

निश्चित रूप से एक मौलिक तनाव है कि आप चीजों को "प्रकार से" या "ऑपरेशन द्वारा" समूहबद्ध करना चाहते हैं या नहीं। सामान्य ओओ तरीका "प्रकार से" होता है जबकि एफपी (कार्यात्मक प्रोग्रामिंग) तरीका "ऑपरेशन द्वारा" होता है।

एक कंपाइलर/दुभाषिया (या ओओ में अधिकांश चीजें विज़िटर पैटर्न पर भरोसा करते हैं) के मामले में, मुझे लगता है कि "ऑपरेशन द्वारा" अधिक प्राकृतिक समूह है। If और And और Or के लिए कोड जेनरेटर सामान्य में थोड़ा सा हो सकता है; विभिन्न नोड्स के टाइपकेकर समान रूप से समानताएं होंगी; यदि आप एक सुंदर प्रिंटर बनाते हैं, तो सभी नोड-सुंदर-प्रिंटिंग कार्यान्वयन के लिए सामान्य रूप से स्वरूपण दिनचर्या होगी। इसके विपरीत, IfElse को प्रिंटिंग, टाइपशेकिंग और कोडगनेनिंग में वास्तव में एक-दूसरे के साथ बहुत कुछ नहीं करना है, तो आप उन्हें IfElse कक्षा में क्यों समूहित करना चाहते हैं?

(अपने प्रश्नों के उत्तर देने के लिए: हाँ, यह मूर्खतापूर्ण है। क्या कोई और तरीका है - हाँ, आप ऐसा कर सकते हैं जैसे आप सी # में करेंगे।मुझे लगता है कि आप पाएंगे कि आप सी # तरीके से बहुत कम खुश हैं, और कोड बिना किसी लाभ के 2-3x बड़ा होगा।)

+1

धन्यवाद - यह वह उत्तर था जिसे मैं ढूंढ रहा था । –

9

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

1

दूसरी चीज जो आप आम तौर पर कर सकते हैं कार्यात्मक भाषा डेटाटाइप पर fold ऑपरेशन को परिभाषित करना है और फिर गुना के संदर्भ में टाइपशेकिंग और कोड जनरेटिंग फ़ंक्शंस को परिभाषित करना है। इस विशेष मामले में मुझे यकीन नहीं है कि यह आपको बहुत खरीदता है क्योंकि फ़ोल्ड फ़ंक्शन में इतने सारे तर्क होंगे कि यह समझना विशेष रूप से आसान नहीं होगा:

let rec fold eqE nonE andE orE ... = function 
| Equality(e1,e2) -> eqE (e1 |> fold eqE nonE ...) (e2 |> fold eqE nonE ...) 
| NonEquality(e1,e2) -> nonE ... 
... 

let inferTypes = fold checkTypes checkTypes checkTypes ... 
+1

क्या यह विज़िटर बनाने के लिए 'गुना' तरीका है? मैं अभी भी ओपी की शैली पसंद करता हूं, जहां inferTypes को सीधे पैटर्न मिलान कथन द्वारा परिभाषित किया जाता है। – Tobu

+2

@Tobu: मुझे व्यक्तिगत रूप से आगंतुक पैटर्न से बहुत कम बदसूरत लगता है। जैसा कि मैंने कहा, इस विशेष उदाहरण में ऐसे कई अलग-अलग मामले हैं जो मुझे नहीं लगता कि एक गुना के मामले में चीजों को परिभाषित करना बेहतर है। आम तौर पर, हालांकि, अक्सर रिकर्सिव डेटाटाइप (जैसे F # की अंतर्निर्मित 'List.fold') पर फ़ोल्ड ऑपरेशंस को परिभाषित करना समझ में आता है। – kvb

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