बड़ी मुसीबत स्थैतिक टाइपिंग
की अड़चन विषम संग्रह और भिन्न प्रकार के कार्य हैं। इसलिए, आरपीसी पुस्तकालयों में, एक दृष्टिकोण अक्सर पाया जाता है जहां इनपुट डेटा एक एडीटी टुकड़ा में होता है, और विधियों के लिए एक ही फ्लैट प्रकार "[फू] -> आईओ फू" है, जिसके कार्यान्वयन डेसरीएलाइजेशन / क्रमांकन कॉपी-पेस्ट करते हैं, जो असुविधाजनक है और त्रुटियों को उत्पन्न करता है। , सहित rantaymovye।
इस समस्या के समाधान ने मुझे हास्केल के व्यावहारिक उपयोग की शुरुआत से लगभग परेशान कर दिया, और आखिरकार, कल रात, प्रेरणा मुझे 6.5 मिलीलीग के रूप में मिली, और सौभाग्य-सत्र और एक बातचीत के बाद, सब कुछ मेरे लिए काम कर गया।
मान लीजिए हम एक सूची बनाना चाहते हैं / शब्दकोश पाठ -> विधि। और तुरंत बुमेर:
methods :: [(String, ????)] methods = [ ("add", \xy -> return (x + y)) , ("reverse", \s -> return (reverse s)) ]
सभी विधियों का एक अलग प्रकार है। यह उन्हें एक अपारदर्शी बॉक्स में रखकर हल किया जा सकता है।
data BaconBox = forall a. a methods :: [(String, BaconBox)]
लेकिन इस तरह के बॉक्स को अनपैक नहीं किया जा सकता है। यह स्पष्ट नहीं है कि आप उनके साथ क्या कर सकते हैं। इसका मतलब है कि हमें उन प्रकारों के एक वर्ग की आवश्यकता है जो इसमें हो सकते हैं और यह वर्णन कर सकते हैं कि फ़ंक्शन को सामान्य रूप में कैसे लाया जाए अर्थात्। इनपुट डेटा को डीरिशियल करने और परिणाम को क्रमबद्ध करने के लिए तैयार के कार्य।
इस उदाहरण में, "प्रोटोकॉल" पढ़ें / शो का उपयोग किया जाएगा। सबसे अच्छा नहीं है, लेकिन बाकी समान है। डेटा प्रकार, क्रमशः, स्ट्रिंग है।
class Tossable t where toss :: [String] -> t -> IO String data BaconBox = forall a. (Tossable a) => BaconBox a
ऐसी परिभाषा के साथ, यह पहले से ही संकलक के लिए भी स्पष्ट है कि किसी भी प्रकार को बॉक्स के अंदर रखा जा सकता है जिसके लिए टॉस फ़ंक्शन को परिभाषित किया गया है, जो ... बाख! यहां हम दूसरी समस्या पर आसानी से चलते हैं। आखिरकार, मैं चाहता हूं कि बुलाए गए तरीके किसी भी तर्क और किसी भी मात्रा में सक्षम हों। यानी ताकि आप बॉक्स में किसी भी आरपीसी हैंडलर को ले सकें और डाल सकें, जिसके लिए मार्शलों की प्रक्रियाओं को परिभाषित किया गया है।
ओलेग और हास्केल विकी में कुछ इसी तरह का एक उदाहरण है - किसी भी प्रकार के किसी भी तर्क के साथ
प्रिंटफ :
www.haskell.org/haskellwiki/Varargs । लेकिन यह बिल्कुल सही नहीं है। लेकिन बिलकुल नहीं! awesome.gif
चाल दो उदाहरणों पर आधारित है - मूल रूप और तर्कों की तह।
मूल रूप निर्धारित करता है कि डेटा एकत्र करने पर क्या करना है। यह विधि के आउटपुट प्रकार के वर्ग को भी परिभाषित करता है।
instance (Show a) => Tossable (IO a) where toss [] f = fmap show f toss _ _ = fail " "
यदि सभी तर्कों का उपयोग किया जाता है और हमारे हाथों में तैयार मान हैं (इस मामले में, यह एक क्रिया है जिसे निष्पादित करने की आवश्यकता है), अर्थात। सभी तर्क फ़ंक्शन पर लागू होते हैं - हम इसे निष्पादित करते हैं और तुरंत परिणाम को क्रमबद्ध करते हैं। शेष तर्कों को फेंक दिया जा सकता है, या आप शपथ ले सकते हैं - मैं शुद्धता के बारे में अनुमान लगाने की तुलना में गलती करना पसंद करता हूं।
तर्कों को ध्वस्त करने के लिए, एक बहुत ही दिलचस्प उदाहरण का उपयोग किया जाता है, अपने सभी महिमा में सामान्य रूप से कार्यात्मक दृष्टिकोण की शक्ति और विशेष रूप से सही प्रकार की प्रणाली का प्रदर्शन करता है। यहां, इनपुट तर्कों का एक वर्ग तय किया गया है, जो आरपीसी विधि को कॉल करने में उपयोग किए जाने वाले प्रकार के लिए एक डीसेरलाइज़र की
सफलता की गारंटी देता है।
instance (Read a, Tossable t) => Tossable (a -> t) where toss [] _ = fail " " toss (a:as) f = toss as (f (read a))
जादुई परिभाषा के बावजूद, जो हो रहा है वह काफी तुच्छ है। यदि हमारा प्रकार एक फ़ंक्शन (a -> t) है, और हमारे पास अभी भी इसके लिए तर्क हैं, तो हम अगले तर्क को उस प्रकार के अनुसार डिसेरलाइज़ करेंगे, जो फ़ंक्शन की अपेक्षा करता है और इसे लागू करता है।
यदि परिणाम आधार के रूप में है, तो यह अच्छा है यदि, फ़ंक्शन को तर्क लागू करने के बाद, फ़ंक्शन फिर से चालू हो जाता है - हम प्रक्रिया को दोहराते हैं।
doAnd :: Bool -> Bool -> IO Bool doAnd ab = return (a && b) doSum3 :: Double -> Double -> Double -> IO Bool doSum3 xyz = return (x + y + z) main = do toss [] (doAnd True True) >>= print toss ["True"] (doAnd True) >>= print toss ["True", "True"] doAnd >>= print toss ["42", "2.71828", "3.14159"] doSum3 >>= print
ए टी वॉयला! फ़ंक्शन शांत रूप से अनपैक्स करता है और तर्कों को फीड करता है और परिणाम पैक करता है।
अंतिम स्पर्श रहता है - बक्से से विधियों का गतिशील प्रेषण। ऐसा करने के लिए, हमारे बॉक्स में थोड़ा मेटाडेटा और उस पर एक खोज फ़ंक्शन जोड़ें:
data BaconBox = forall a. (Tossable a) => BaconBox String a tossBacon :: (Show a) => [BaconBox] -> String -> [String] -> IO a tossBacon [] _ _ = fail " " tossBacon (BaconBox bn bf : bs) name args | bn == name = toss args bf | otherwise = tossBacon bs name args
एक विशेष फ़ंक्शन की आवश्यकता होती है ताकि कंपाइलर फट न जाए (sic!) जब पैटर्न के मिलान के अलावा किसी भी तरह से एक अस्तित्वगत कंटेनर खोलते हैं। FP- दुनिया की समस्याएं ...
तो, हमारे पास एक कंटेनर है जो आपको मनमाने ढंग से (लेकिन इनपुट और आउटपुट में सख्ती से सीमित) प्रकार के साथ एक फ़ंक्शन को अंदर लेने की अनुमति देता है और उन्हें नाम से बुलाता है, स्वचालित रूप से मार्शलिंग रूटीन कार्य करता है। कम कोड का मतलब है कम कीड़े। हुर्रे!