2013-10-05 4 views
9

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

कोई कोड नमूना नहीं है क्योंकि सवाल अधिक वैचारिक है, लेकिन हास्केल में नमूना के साथ उत्तर (किसी भी एफआरपी ढांचे का उपयोग करके) या किसी अन्य भाषा का स्वागत है।

+1

मुझे लगता है कि यह प्रश्न एक विशिष्ट उत्तर देने के लिए बहुत व्यापक है। आपके द्वारा सुझाए गए रणनीतियों में से कोई भी (सिंकिंग, एफआरपी, वैश्विक युद्ध) दी गई स्थिति के लिए उपयुक्त हो सकता है। या संभवतः एक स्थानीय रूप से साझा 'आईओआरआईफ़' या 'एमवर'। या यदि गणना वास्तव में एक धागे में हैं, तो 'स्टेटटी' मोनैड ट्रांसफार्मर। यह मुझे स्पष्ट नहीं है कि 'थ्रेड" का अर्थ 'फोर्कियो' द्वारा बनाए गए वास्तविक धागे हैं, या यदि वे सख्ती से वैचारिक हैं और आप वास्तव में केवल एक थ्रेड चला रहे हैं। –

+2

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

उत्तर

13

मैं इस समस्या के समाधान पर काम कर रहा हूं।

ए) एक शुद्ध और एकल पिरोया विनिर्देश

बी) एकल पिरोया विनिर्देश StateT का उपयोग करता है आम राज्य साझा करने के लिए

में अपने सभी समवर्ती कोड शुद्ध: उच्च-स्तरीय सारांश यह है कि आप है समग्र वास्तुकला मॉडल-व्यू-नियंत्रक द्वारा प्रेरित है। आपके पास:

  • नियंत्रकों, जो effectful आदानों
  • दृश्य है, जो effectful outputs हैं
  • एक मॉडल है, जो एक शुद्ध धारा परिवर्तन

मॉडल केवल एक नियंत्रक के साथ बातचीत कर सकते हैं कर रहे हैं और एक दृश्य। हालांकि, दोनों नियंत्रक और विचार मोनोइड्स हैं, इसलिए आप एकाधिक नियंत्रकों को एकल नियंत्रक और एकाधिक दृश्यों को एक ही दृश्य में जोड़ सकते हैं। रेखचित्र, यह इस तरह दिखता है:

controller1 -           -> view1 
       \          /
controller2 ---> controllerTotal -> model -> viewTotal---> view2 
      /          \ 
controller3 -           -> view3 

        \______ ______/ \__ __/ \___ ___/ 
         v    v   v 
        Effectful  Pure Effectful 

मॉडल एक शुद्ध, एकल पिरोया धारा ट्रांसफार्मर कि Arrow और ArrowChoice लागू करता है। , मैं धक्का-आधारित pipes का उपयोग

  • Arrow समानांतरवाद
  • ArrowChoice को एकल पिरोया बराबर है संगामिति

इस मामले में करने के लिए एकल पिरोया बराबर है,: कारण है कि वह यह है कि जो एक सही Arrow और ArrowChoice उदाहरण प्रतीत होता है, हालांकि मैं अभी भी कानूनों की पुष्टि करने पर काम कर रहा हूं, इसलिए यह समाधान तब भी प्रयोगात्मक है जब तक कि मैं अपने सबूत पूरा नहीं कर लेता। जो उत्सुक हैं, उनके लिए प्रासंगिक प्रकार और उदाहरण हैं:

newtype Edge m r a b = Edge { unEdge :: a -> Pipe a b m r } 

instance (Monad m) => Category (Edge m r) where 
    id = Edge push 
    (Edge p2) . (Edge p1) = Edge (p1 >~> p2) 

instance (Monad m) => Arrow (Edge m r) where 
    arr f = Edge (push />/ respond . f) 
    first (Edge p) = Edge $ \(b, d) -> 
     evalStateP d $ (up \>\ unsafeHoist lift . p />/ dn) b 
     where 
     up() = do 
      (b, d) <- request() 
      lift $ put d 
      return b 
     dn c = do 
      d <- lift get 
      respond (c, d) 

instance (Monad m) => ArrowChoice (Edge m r) where 
    left (Edge k) = Edge (bef >=> (up \>\ (k />/ dn))) 
     where 
      bef x = case x of 
       Left b -> return b 
       Right d -> do 
        _ <- respond (Right d) 
        x2 <- request() 
        bef x2 
      up() = do 
       x <- request() 
       bef x 
      dn c = respond (Left c) 

मॉडल को एक मोनड ट्रांसफार्मर भी होना चाहिए। कारण यह है कि हम साझा स्थिति का ट्रैक रखने के लिए बेस मोनैड में StateT एम्बेड करना चाहते हैं। इस मामले में, pipes बिल फिट बैठता है।

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

Link to Github Repository

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

मैंने rcpl का पूरा तर्क लिया और इसे एक एकल, शुद्ध पाइप में बदल दिया। this module में आपको यही मिलेगा, और कुल तर्क पूरी तरह से rcplCore pipe के भीतर निहित है।

यह साफ है, क्योंकि अब कार्यान्वयन शुद्ध है, मैं इसे जल्दी से जांच सकता हूं और कुछ गुणों को सत्यापित कर सकता हूं!

>>> quickCheck $ \n -> length ((`evalState` initialStatus) $ P.toListM $ each (replicate n (Key 'x')) >-> runEdge (rcplCore t)) == n || n < 0 

n बार है कि मैं प्रेस की संख्या है: उदाहरण के लिए, एक संपत्ति मैं quickcheck करना चाह सकते हैं है कि वहाँ जो मैं इस तरह बनाना होगा x कुंजी, के उपयोगकर्ता कुंजी दबाएँ प्रति ठीक एक टर्मिनल आदेश है x कुंजी। उस परीक्षण को चलाने से निम्नलिखित आउटपुट उत्पन्न होता है:

*** Failed! Falsifiable (after 17 tests and 6 shrinks): 
78 

क्विक चेक ने पाया कि मेरी संपत्ति झूठी थी! इसके अलावा, क्योंकि कोड संदर्भित रूप से पारदर्शी है, क्विक चेक कम से कम पुनरुत्पादन उल्लंघन के लिए काउंटररेक्स नमूना को कम कर सकता है। 78 कुंजी प्रेस के बाद टर्मिनल ड्राइवर एक नई लाइन उत्सर्जित करता है क्योंकि कंसोल 80 वर्ण चौड़ा है, और दो अक्षर प्रॉम्प्ट द्वारा उठाए जाते हैं ("> " इस मामले में)। इस तरह की संपत्ति है कि अगर मुझे समेकन और IO ने मेरी पूरी प्रणाली को संक्रमित किया तो मुझे बहुत मुश्किल समझना होगा।

शुद्ध सेटअप होने के कारण किसी अन्य कारण के लिए बहुत अच्छा है: सब कुछ पूरी तरह से पुन: उत्पन्न होता है! अगर मैं सभी आने वाली घटनाओं का लॉग संग्रह करता हूं, तो किसी भी समय एक बग है, मैं घटनाओं को फिर से चला सकता हूं और टेस्ट केस को पूरी तरह से पुन: पेश कर सकता हूं जिसे मैं अपने टेस्ट सूट में जोड़ सकता हूं।

हालांकि, वास्तव में शुद्धता का सबसे महत्वपूर्ण लाभ कोड के बारे में अनौपचारिक रूप से और औपचारिक रूप से अधिक आसानी से कारण होने की क्षमता है। जब आप समीकरण से हास्केल के शेड्यूलर को हटाते हैं तो आप अपने कोड के बारे में चीजें स्थिर रूप से साबित कर सकते हैं, जिसे आप साबित नहीं कर पाएंगे कि आपको एक समवर्ती रूप से निर्दिष्ट अर्थशास्त्र के साथ एक समवर्ती रनटाइम पर निर्भर होना है। यह वास्तव में अनौपचारिक तर्क के लिए भी वास्तव में उपयोगी साबित हुआ, क्योंकि जब मैंने mvc का उपयोग करने के लिए अपना कोड बदल दिया था, तब भी इसमें कई कीड़े थीं, लेकिन इन्हें पहले पुनरावृत्ति से जिद्दी समवर्ती बगों से डीबग करना और निकालना बहुत आसान था।

rcpl उदाहरण विभिन्न घटकों के बीच वैश्विक राज्य साझा करने के लिए StateT का उपयोग करता है, तो अपने प्रश्न का घना जवाब है: आप StateT उपयोग कर सकते हैं, लेकिन केवल तभी जब आप एक एकल पिरोया संस्करण के लिए अपने सिस्टम को बदलने। सौभाग्य से यह संभव है!

+1

मुझे खेद है लेकिन मॉडल और प्रकार 'एज' के बीच क्या संबंध है? क्या संपूर्ण एमवीसी आरेख एक 'एज' है? – chibro2

+1

हाँ, 'एज' को वास्तव में 'मॉडल' कहा जाना चाहिए। नाम अभी भी प्रवाह में हैं। इसके अलावा, हाँ, पूरा एमवीसी आरेख सिर्फ एक बड़ा 'एज' है। –

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

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