2009-03-03 17 views
54

ईश्वर मुझे "कोड गंध" शब्द से नफरत है, लेकिन मैं कुछ और सटीक नहीं सोच सकता।हास्केल राज्य मोनड का उपयोग कोड गंध?

मैं अपने खाली समय संकलक निर्माण, भाषा डिजाइन, और कार्यात्मक प्रोग्रामिंग के बारे में जानने के लिए एक उच्च स्तरीय भाषा & Whitespace को संकलक डिजाइनिंग कर रहा हूँ (संकलक हास्केल में लिखा जा रहा है)।

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

मुझे बताया गया है कि एक कंपाइलर लिखना एक समस्या है जो कार्यात्मक प्रतिमान के लिए उपयुक्त है, लेकिन मुझे लगता है कि मैं इसे उसी तरह डिजाइन कर रहा हूं जिस तरह से मैं इसे सी में डिजाइन करूंगा (आप वास्तव में सी लिख सकते हैं कोई भी भाषा - यहां तक ​​कि हास्केल डब्ल्यू/राज्य मोनैड)।

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

+11

WTF मैन, व्हाइटस्पेस ... –

+9

क्या मुझे mips या x86 asm के लिए एक कंपाइलर लिखना चाहिए? यह काफी जटिल होगा। – Cybis

+22

सी ++, साइबिस। 1 999 विनिर्देश। और हम इसे शुक्रवार तक चाहते हैं। –

उत्तर

40

मैं कहूंगा कि सामान्य रूप से स्थिति कोड गंध नहीं है, जब तक कि इसे छोटा और अच्छी तरह से नियंत्रित रखा जाता है।

इसका मतलब है कि राज्य, एसटी या कस्टम-निर्मित वाले मोनैड का उपयोग करना, या केवल कुछ डेटा स्थानों पर डेटा डेटा युक्त डेटा संरचना है, जो एक बुरी चीज नहीं है।(असल में, मोनैड सिर्फ यह करने में सहायता कर रहे हैं!) हालांकि, ऐसी जगह होने पर जो पूरे स्थान पर जाता है (हाँ, इसका मतलब है, आईओ मोनड!) एक बुरी गंध है।

इसका एक स्पष्ट उदाहरण था जब मेरी टीम ICFP Programming Contest 2009 के लिए हमारी प्रविष्टि पर काम कर रही थी (कोड गिट: //git.cynic.net/haskell/icfp-contest-2009 पर उपलब्ध है)।

  • वीएम: आभासी मशीन है कि अनुकार कार्यक्रम
  • नियंत्रकों भाग गया: कि सिम्युलेटर के उत्पादन में पढ़ सकते हैं और नए नियंत्रण आदानों
  • उत्पन्न दिनचर्या के कई अलग अलग सेट हम यह करने के लिए कई अलग अलग मॉड्यूलर भागों के साथ समाप्त हो गया
  • समाधान: समाधान फ़ाइल की पीढ़ी नियंत्रक
  • Visualizers के उत्पादन के आधार पर: दिनचर्या है कि दोनों इनपुट और आउटपुट बंदरगाहों पढ़ सकते हैं और दृश्य या क्या सिमुलेशन के रूप में चल रहा था की लॉग किसी प्रकार उत्पन्न के कई अलग अलग सेट प्रगति

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

यहां मुख्य बिंदु यह था कि किसी भी विशेष राज्य के आंतरिक अपने विशेष मॉड्यूल तक ही सीमित थे, और प्रत्येक मॉड्यूल को अन्य मॉड्यूल के लिए राज्य के अस्तित्व के बारे में कुछ नहीं पता था। राज्य में कुछ हद तक डेटा आइटम के साथ, राज्य कोड और डेटा का कोई भी विशेष सेट आम तौर पर केवल कुछ दर्जन लाइनों का होता था।

यह सब एक दर्जन लाइनों के एक छोटे से काम में एक साथ चिपकाया गया था, जिसमें किसी भी राज्य के आंतरिक लोगों तक पहुंच नहीं थी, और जिसने सिमुलेशन के माध्यम से उचित क्रम में सही चीजों को सही कहा था, और प्रत्येक मॉड्यूल (मॉड्यूल के पिछले राज्य के साथ, निश्चित रूप से) के बाहर बाहरी जानकारी की एक सीमित मात्रा में पारित किया।

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

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

+0

बहुत अच्छा जवाब है, हालांकि मुझे यकीन नहीं है कि दो महीने बाद "स्वीकृत" उत्तर बदलना उचित है। परवाह किए बिना बहुत धन्यवाद। – Cybis

+0

मैंने यह दिखाने के लिए एक नया पैराग्राफ जोड़ा है कि मुझे लगता है कि स्वीकार्य उत्तर क्यों है, आगे प्रतिबिंब पर, वास्तव में गलत जवाब। मुझे लगता है कि आपको इसे बदलना चाहिए; मैं हमेशा सूची के शीर्ष पर बैठे गलत उत्तरों से नाराज हूं। –

+0

मैंने उस पेपर के आधार पर स्वीकार किया जो उसने लिंक किया था, उत्तर ही नहीं (मैं सहमत हूं कि अकेले जवाब काफी खराब था)। अब मैंने इसके बारे में फिर से सोचा है, शायद यह पर्याप्त कारण नहीं था। मोनैड का उचित उपयोग मॉड्यूलरिटी (आपका उत्तर) और अमूर्तता (नॉर्मन रैमसे का उत्तर) दोनों के बारे में है। अगर मैं इसे स्वीकार करता हूं, तो दोनों शीर्ष पर होंगे। यह सबसे अच्छा होगा। – Cybis

-5

ठीक है, monads का उपयोग न करें। कार्यात्मक प्रोग्रामिंग की शक्ति कार्य शुद्धता और उनका पुन: उपयोग है। इस पेपर में एक बार मेरे प्रोफेसर ने लिखा है और वह उन लोगों में से एक है जिन्होंने हास्केल बनाने में मदद की।

पेपर को "Why functional programming matters" कहा जाता है, मेरा सुझाव है कि आप इसे पढ़ लें। यह एक अच्छा पढ़ा है।

+2

मैंने सोचा था कि monads का overuse एक कोड गंध था। बहुत सारे ऑनलाइन हास्केल ट्यूटोरियल हैं, लेकिन अच्छे हास्केल प्रोग्रामिंग के बारे में बहुत कम हैं। कागज के लिए बहुत धन्यवाद। – Cybis

+16

केवल आईओ मोनड किसी भी तरह से अशुद्ध है। –

+2

ध्यान दें कि "क्यों फंक्शनल प्रोग्रामिंग मैटर्स" के लेखक जॉन ह्यूजेस "मोनैड्स टू एरोज़ को सामान्य बनाना" के लेखक भी हैं, जो राज्य को संभालने का एक तरीका भी है। –

-12

चलो यहाँ शब्दावली के बारे में सावधान रहें। राज्य प्रति बुरा नहीं है; कार्यात्मक भाषाओं में राज्य है। "कोड गंध" क्या होती है जब आप खुद को वैरिएबल मान असाइन करना चाहते हैं और उन्हें बदलना चाहते हैं।

बेशक, हास्केल राज्य मोनैड इसी कारण से है - जैसे I/O, यह आपको एक बाधित संदर्भ में असुरक्षित और अन-कार्यात्मक चीजें करने देता है।

तो, हाँ, यह शायद एक कोड गंध है।

+16

वह मिथक क्यों दोहराया जाता है? अधिकांश पारंपरिक मोनैड न तो "असुरक्षित" हैं, न ही "अन-कार्यात्मक" हैं। यहां तक ​​कि आईओ, जो कार्यान्वयन/कार्यान्वयन/वास्तव में गैर-कार्यात्मक है, क्योंकि इसे विशेष रूप से संकलक द्वारा संभाला जाता है, इस तरह माना जा सकता है। – squadette

+0

खैर, मुख्य रूप से क्योंकि, परिभाषा के अनुसार, यह सच है। यहां तक ​​कि सबसे परिष्कृत कंपाइलर में, आई/ओ घटक, आवश्यकता के अनुसार, साइड इफेक्ट्स होना चाहिए: अंतर्निहित डिवाइस की स्थिति बदलनी चाहिए, अन्यथा कोई I/O नहीं हुआ है। –

+8

आईओ मोनड (और आईआईआरसी एसटी मोनैड) प्रदर्शन के उद्देश्य के लिए आंतरिक रूप से गैर-कार्यात्मक है। हालांकि, यह होना जरूरी नहीं है।रनटाइम पर्यावरण (सी कोड) केवल हास्केल कोड के बिना मोनैड निष्पादित कर सकता है, जो कभी भी असुरक्षित या अन-कार्यात्मक कुछ भी कर रहा है। अन्य सभी monads सभी असुरक्षित या गैर-कार्यात्मक नहीं हैं। – Zifre

6

क्या आपने Attribute grammars (एजी) देखा है? (मोनैड रीडर में wikipedia और article पर अधिक जानकारी)?

एजी के साथ आप गुण को वाक्यविन्यास पेड़ में जोड़ सकते हैं। इन गुणों को संश्लेषित और विरासत में विशेषताओं में विभाजित किया गया है।

समन्वित विशेषताओं चीजें आप अपने वाक्य रचना पेड़ से उत्पन्न (या संश्लेषण) कर रहे हैं, यह उत्पन्न कोड, या सभी टिप्पणियों, वरना आपके जो कुछ में रुचि हो सकती है।

विरासत में प्राप्त गुण आपके वाक्य रचना पेड़ के लिए इनपुट कर रहे हैं, यह पर्यावरण उत्पादन, या कोड पीढ़ी के दौरान उपयोग करने के लिए लेबल की एक सूची हो सकती है।

यूट्रेक्ट विश्वविद्यालय में हम संकलक लिखने के लिए Attribute Grammar System (UUAGC) का उपयोग करते हैं।यह एक प्री-प्रोसेसर है जो प्रदान की गई .ag फ़ाइलों से हैकेल कोड (.hs फ़ाइलें) उत्पन्न करता है।


हालांकि, अगर आप अभी भी हास्केल सीख रहे हैं, तो शायद यह नहीं है कि अधिक अमूर्त की अभी तक एक और परत सीखने शुरू करने के लिए समय है।

उस मामले में, आप मैन्युअल रूप से, कि विशेषताओं का कोड की तरह व्याकरण आपके लिए जनरेट लिख सकता है उदाहरण के लिए:

data AbstractSyntax = Literal Int | Block AbstractSyntax 
        | Comment String AbstractSyntax 

compile :: AbstractSyntax -> [Label] -> (Code, Comments) 
compile (Literal x) _  = (generateCode x, []) 
compile (Block ast) (l:ls) = let (code', comments) = compile ast ls 
          in (labelCode l code', comments) 
compile (Comment s ast) ls = let (code, comments') = compile ast ls 
          in (code, s : comments') 

generateCode :: Int -> Code 
labelCode :: Label -> Code -> Code 
+0

निश्चित रूप से कुछ ध्यान में रखना, धन्यवाद। मेरी भाषा वास्तव में सभी जटिल नहीं है - यह बहुत लापरवाही की तरह है लेकिन मैक्रोज़, सूचियों, या उच्च-आदेश कार्यों के बिना (व्हाईटस्पेस में अनुवाद करना मुश्किल होगा)। विशेषता व्याकरण का उपयोग थोड़ा अधिक हो सकता है। – Cybis

+0

उस स्थिति में आप निश्चित रूप से मैनुअल एजी पैटर्न का उपयोग कर सकते हैं। यह राज्य मोनड की जरूरत को खत्म कर देगा। –

43

मैं हास्केल में कई compilers लिखा है, और एक राज्य इकाई के एक उचित समाधान है कई संकलक समस्याओं के लिए। लेकिन आप इसे अमूर्त रखना चाहते हैं --- यह स्पष्ट न करें कि आप एक मोनड का उपयोग कर रहे हैं।

ग्लासगो हास्केल कंपाइलर (जो मैंने नहीं लिखने के लिए एक उदाहरण दिया है; मैं बस कुछ किनारों के आसपास काम करता हूं), जहां हम नियंत्रण-प्रवाह ग्राफ बनाते हैं। यहाँ रेखांकन बनाने के लिए बुनियादी तरीके हैं:

empyGraph :: Graph 
mkLabel  :: Label -> Graph 
mkAssignment :: Assignment -> Graph -- modify a register or memory 
mkTransfer :: ControlTransfer -> Graph -- any control transfer 
(<*>)  :: Graph -> Graph -> Graph 

लेकिन जैसा कि आप की खोज की है, अद्वितीय लेबल की आपूर्ति बनाए रखने में सबसे अच्छा थकाऊ है, इसलिए हम साथ ही इन कार्यों प्रदान करते हैं:

withFreshLabel :: (Label -> Graph) -> Graph 
mkIfThenElse :: (Label -> Label -> Graph) -- branch condition 
      -> Graph -- code in the 'then' branch 
      -> Graph -- code in the 'else' branch 
      -> Graph -- resulting if-then-else construct 

पूरी Graph चीज एक अमूर्त प्रकार है, और अनुवादक पूरी तरह से कार्यात्मक फैशन में ग्राफों को मजबूती से बनाता है, बिना किसी अज्ञात बात के कि कुछ भी चल रहा है। फिर, जब अंततः ग्राफ का निर्माण किया जाता है, इसे बीजगणित डेटाटाइप में बदलने के लिए हम कोड उत्पन्न कर सकते हैं, हम इसे अद्वितीय लेबल की आपूर्ति देते हैं, राज्य मोनड चलाते हैं, और डेटा संरचना खींचते हैं।

राज्य मोनड नीचे छिपा हुआ है; हालांकि यह ग्राहक के संपर्क में नहीं है, Graph की परिभाषा कुछ इस तरह है:

type Graph = RealGraph -> [Label] -> (RealGraph, [Label]) 

या थोड़ा अधिक सही

type Graph = RealGraph -> State [Label] RealGraph 
    -- a Graph is a monadic function from a successor RealGraph to a new RealGraph 

राज्य इकाई अमूर्त की एक परत के पीछे छिपा हुआ के साथ, यह बदबूदार नहीं है बिलकुल!

3

यह आपको एक इकाई के बजाय एक अनुप्रयोगी functor चाहते हो सकता है कि संभव है:

http://www.haskell.org/haskellwiki/Applicative_functor

मुझे लगता है कि मूल पत्र बताते हैं यह विकि की तुलना में बेहतर है, हालांकि:

http://www.soi.city.ac.uk/~ross/papers/Applicative.html

0

सामान्य रूप से आपको जहां भी संभव हो राज्य से बचने की कोशिश करनी चाहिए, लेकिन यह हमेशा व्यावहारिक नहीं है। Applicative प्रभावशाली कोड को अच्छे और अधिक कार्यात्मक दिखता है, विशेष रूप से पेड़ ट्रैवर्सल कोड इस शैली से लाभ उठा सकता है। नाम पीढ़ी की समस्या के लिए अब एक अच्छा पैकेज उपलब्ध है: value-supply

2

मुझे नहीं लगता कि राज्य मोनाड का उपयोग कोड कोड गंध है जब यह राज्य मॉडल के लिए उपयोग किया जाता है।

यदि आपको अपने कार्यों के माध्यम से थ्रेड स्टेटस की आवश्यकता है, आप इसे स्पष्ट रूप से कर सकते हैं, राज्य को तर्क के रूप में ले कर और प्रत्येक फ़ंक्शन में इसे वापस कर सकते हैं। राज्य मोनाड एक अच्छा अमूर्तता प्रदान करता है: यह आपके लिए राज्य को पास करता है और राज्यों की आवश्यकता वाले कार्यों को गठबंधन करने के लिए बहुत उपयोगी कार्य प्रदान करता है। इस मामले में, राज्य मोनाड (या आवेदक) का उपयोग कोड गंध नहीं है।

हालांकि, यदि आप प्रोग्रामिंग की एक अनिवार्य शैली का अनुकरण करने के लिए राज्य मोनाड का उपयोग करते हैं, जबकि एक कार्यात्मक समाधान पर्याप्त होगा, तो आप केवल चीजों को जटिल बना रहे हैं।

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