अपने मौद्रिक कार्यों में सत्यापन के साथ त्रुटि मोनाद का उपयोग करने के लिए बेहतर है, या अपने स्वयं के सन्यासी को सीधे अपने बाइंड में सत्यापन के साथ लागू करें?


9

मैं सोच रहा हूं कि प्रयोज्यता / रखरखाव के लिए बेहतर डिज़ाइन वार क्या है, और समुदाय के साथ फिटिंग करने के लिए क्या बेहतर है।

डेटा मॉडल दिया:

type Name = String

data Amount = Out | Some | Enough | Plenty deriving (Show, Eq)
data Container = Container Name deriving (Show, Eq)
data Category = Category Name deriving (Show, Eq)
data Store = Store Name [Category] deriving (Show, Eq)
data Item = Item Name Container Category Amount Store deriving Show
instance Eq (Item) where
  (==) i1 i2 = (getItemName i1) == (getItemName i2)

data User = User Name [Container] [Category] [Store] [Item] deriving Show
instance Eq (User) where
  (==) u1 u2 = (getName u1) == (getName u2)

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

तो, मैं बस चाहिए:

  • इसे एक त्रुटि मोनड में लपेटें और मानदंड कार्यों को मान्यता निष्पादित करें
  • इसे एक त्रुटि मोनाड में लपेटें और उपभोक्ता को इस क्रम में एक मोनिडिक सत्यापन फ़ंक्शन को बांधें जो उचित त्रुटि प्रतिक्रिया को फेंकता है (इसलिए वे अमान्य उपयोगकर्ता ऑब्जेक्ट को मान्य और ले जाने के लिए नहीं चुन सकते हैं)
  • वास्तव में इसे उपयोगकर्ता पर एक बाँध उदाहरण में प्रभावी ढंग से बनाने के लिए अपनी तरह का त्रुटि मोनाद बना रहा है जो हर बाइंड के साथ सत्यापन को स्वचालित रूप से निष्पादित करता है

मैं 3 दृष्टिकोणों में से प्रत्येक के लिए सकारात्मक और नकारात्मक देख सकता हूं लेकिन यह जानना चाहता हूं कि समुदाय द्वारा इस परिदृश्य के लिए अधिक सामान्यतः क्या किया जाता है।

तो कोड में कुछ इस तरह है, विकल्प 1:

addStore s (User n1 c1 c2 s1 i1) = validate $ User n1 c1 c2 (s:s1) i1
updateUsersTable $ someUser >>= addStore $ Store "yay" ["category that doesnt exist, invalid argh"]

विकल्प 2:

addStore s (User n1 c1 c2 s1 i1) = Right $ User n1 c1 c2 (s:s1) i1
updateUsersTable $ Right someUser >>= addStore $ Store "yay" ["category that doesnt exist, invalid argh"] >>= validate
-- in this choice, the validation could be pushed off to last possible moment (like inside updateUsersTable before db gets updated)

विकल्प 3:

data ValidUser u = ValidUser u | InvalidUser u
instance Monad ValidUser where
    (>>=) (ValidUser u) f = case return u of (ValidUser x) -> return f x; (InvalidUser y) -> return y
    (>>=) (InvalidUser u) f = InvalidUser u
    return u = validate u

addStore (Store s, User u, ValidUser vu) => s -> u -> vu
addStore s (User n1 c1 c2 s1 i1) = return $ User n1 c1 c2 (s:s1) i1
updateUsersTable $ someValidUser >>= addStore $ Store "yay" ["category that doesnt exist, invalid argh"]

जवाबों:


5

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

यदि यह एक वैध परिदृश्य है, तो रनटाइम के दौरान कुछ त्रुटि प्रसंस्करण उपयुक्त है। तब मैं पूछना चाहता हूँ: क्या यह वास्तव में मेरे लिए क्या मतलब है कि एक Userहै अवैध ?

  1. क्या इसका मतलब है कि एक अमान्य Userकुछ कोड को विफल कर सकता है? क्या आपके कोड के हिस्से इस बात पर भरोसा करते हैं कि एक Userहमेशा मान्य होता है?
  2. या इसका मतलब सिर्फ इतना है कि इसकी एक विसंगति जिसे बाद में ठीक करने की आवश्यकता है, लेकिन गणना के दौरान कुछ भी नहीं टूटता है?

यदि यह 1. है, तो मैं निश्चित रूप से किसी प्रकार की त्रुटि के लिए जाऊंगा (या तो मानक या अपना खुद का), अन्यथा आप गारंटी खो देंगे कि आपका कोड ठीक से काम कर रहा है।

अपना खुद का मोनाड बनाना या मोनाड ट्रांसफॉर्मर के ढेर का उपयोग करना एक और मुद्दा है, शायद यह मददगार होगा: क्या कभी किसी ने जंगली में मोनाड ट्रांसफार्मर का सामना किया है?


अपडेट: अपने विस्तारित विकल्पों को देखते हुए:

  1. जाने का सबसे अच्छा तरीका लगता है। शायद, वास्तव में सुरक्षित होने के लिए, मैं बल्कि के कंस्ट्रक्टर को Userछिपाऊंगा और इसके बजाय केवल कुछ फ़ंक्शन निर्यात करूंगा जो एक अवैध उदाहरण बनाने की अनुमति नहीं देते हैं। इस तरह आपको यकीन होगा कि किसी भी समय ऐसा होता है कि यह ठीक से संभाला जाता है। उदाहरण के लिए, बनाने के लिए एक सामान्य कार्य Userकुछ ऐसा हो सकता है

    user :: ... -> Either YourErrorType User
    -- more generic:
    user :: (MonadError YourErrorType m) ... -> m User
    -- Or if you actually don't need to differentiate errors:
    user :: ... -> Maybe User
    -- or more generic:
    user :: (MonadPlus m) ... -> m User
    -- etc.
    

    उदाहरण के लिए Map, कई पुस्तकालय समान रूप से लागू होते हैं , Setया Seqअंतर्निहित कार्यान्वयन को छिपाते हैं, ताकि ऐसा ढांचा बनाना संभव न हो जो उनके आक्रमणकारियों का पालन नहीं करता हो।

  2. यदि आप सत्यापन को बहुत अंत तक स्थगित करते हैं, और Right ...हर जगह उपयोग करते हैं, तो आपको किसी भी मोनाड की आवश्यकता नहीं है। आप बस शुद्ध संगणना कर सकते हैं और अंत में किसी भी संभावित त्रुटियों को हल कर सकते हैं। IMHO यह दृष्टिकोण बहुत जोखिम भरा है, क्योंकि एक अमान्य उपयोगकर्ता मूल्य के कारण कहीं और अवैध डेटा हो सकता है, क्योंकि आपने गणना को जल्द ही बंद नहीं किया था। और, अगर ऐसा होता है कि कोई अन्य विधि उपयोगकर्ता को अपडेट करती है ताकि यह फिर से मान्य हो, तो आप कहीं न कहीं अमान्य डेटा के साथ समाप्त हो जाएंगे और इसके बारे में जानेंगे भी नहीं।

  3. यहां कई समस्याएं हैं।

    • सबसे महत्वपूर्ण यह है कि एक सन्यासी को किसी भी प्रकार के पैरामीटर को स्वीकार करना चाहिए, न कि केवल User। तो आपके validateपास u -> ValidUser uबिना किसी प्रतिबंध के टाइप करना होगा u। इसलिए इस तरह के एक सनक को लिखना संभव नहीं है जो इनपुट्स को मान्य करता है return, क्योंकि returnपूरी तरह से बहुरूपी होना चाहिए।
    • अगला, जो मुझे समझ में नहीं आता है वह यह है कि आप case return u ofकिस की परिभाषा में मेल खाते हैं >>=। का मुख्य बिंदु ValidUserवैध और अवैध मूल्यों भेद करने के लिए होना चाहिए, और इसलिए इकाई सुनिश्चित करना चाहिए कि यह हमेशा सच है। तो यह बस हो सकता है

      (>>=) (ValidUser u) f = f u
      (>>=) (InvalidUser u) f = InvalidUser u
      

    और यह पहले से ही बहुत पसंद है Either

आम तौर पर, मैं एक कस्टम मोनाड का उपयोग केवल तभी करूँगा

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

आपके अंतिम दो अंक अमूल्य हैं और मैंने उनके बारे में नहीं सोचा है! निश्चित रूप से जिस ज्ञान की मुझे तलाश थी, अपने विचारों को साझा करने के लिए धन्यवाद, मैं निश्चित रूप से # 1 के साथ जाऊंगा!
जिमी हॉफ

बस कल रात पूरे मॉड्यूल को बांध दिया और आप सही मर चुके थे। मैंने अपनी वैध विधि को कम संख्या में उन प्रमुख कॉम्बिनेटरों में शामिल किया, जो मैंने सभी मॉडल अपडेट किए थे और यह वास्तव में इसके लिए बहुत अधिक समझ में आता है। मैं वास्तव में # 3 के बाद जाने वाला था और अब मैं देखता हूं कि कैसे ... अनम्य कि दृष्टिकोण होगा, इसलिए मुझे सीधे सेट करने के लिए एक टन धन्यवाद!
जिमी हॉफ 19
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.