2017-01-19 4 views
6

मेरी डाटामूल्य फ़ील्ड को किसी फ़ील्ड को सीमित करने के लिए कैसे करें?

data User = User { uRank :: Int, uProgress :: Int } 

में मैं मान [-1, 1, 3], उदाहरण के लिए की एक सूची के लिए uRank सीमित करना चाहते हैं।

मैं यह कैसे कर सकता हूं?

+4

क्या यह एक ऐसा नहीं है जो आप enu के साथ कर सकते हैं मी और एक फ़ंक्शन का उपयोग करें जो इसे इट्स पर मैप करता है यदि आप उन पर अंकगणित करना चाहते हैं? –

उत्तर

9

एक छोटे से योग प्रकार को परिभाषित करना इस विशिष्ट प्रश्न का सबसे अच्छा जवाब है। आप इस प्रभाव को प्राप्त करने के लिए स्मार्ट कन्स्ट्रक्टर के साथ newtype का भी उपयोग कर सकते हैं।

newtype Rank = UnsafeMkRank { unRank :: Int } 

mkRank :: Int -> Maybe Rank 
mkRank i 
    | i `elem` [-1, 1, 3] = Just (UnsafeMkRank i) 
    | otherwise = Nothing 

अब, आप केवल सुरक्षित mkRank निर्माता का उपयोग प्रदान की है, तो आप मान सकते हैं कि आपके Rank मूल्यों Int मूल्यों आप चाहते हैं।

+1

स्मार्ट कन्स्ट्रक्टर अक्सर मॉड्यूल निर्यात सूचियों के संयोजन के साथ प्रयोग किया जाता है: मॉड्यूल MyMod (रैंक, myRank) जहां .. कन्स्ट्रक्टर UnsafeMkRank निर्यात नहीं करेगा, आपको अन्य मॉड्यूल में इसका उपयोग करने से मना कर देगा, अगर आप (या अन्य उपयोगकर्ता) कभी भी अच्छी रैंक बनाने के लिए भूल जाओ। –

8

कुछ के लिए इतना छोटा है, तो आप एक सटीक प्रकार को परिभाषित करना चाहिए:

data Rank = RankNegOne | RankOne | RankThree -- There are probably better names 
data User = User { uRank :: Rank, uProgress :: Int } 

हास्केल पूर्णांकों का एक सबसेट के रूप में सब कुछ सांकेतिक शब्दों में बदलना करने के लिए आप के लिए मजबूर नहीं करता है; इस का लाभ ले!

बड़े सबसेट्स के लिए जहां यह अनावश्यक होगा, आप आश्रित प्रकार की तलाश कर रहे हैं, जो (हाल ही में?) सीधे हास्केल द्वारा समर्थित नहीं है।

+0

क्या यहां उपयोग में निर्भर प्रकार का त्वरित उदाहरण दिखाना संभव है? वास्तव में रैंक मूल्य के लिए बहुत सारे मूल्य हैं। – osager

+1

यह संभव है, लेकिन मैं नहीं हूं :) मैं अस्पष्ट होने से अपनी अज्ञानता को घुमाने का प्रयास कर रहा था। – chepner

+0

यह ठीक है। कम से कम यह एक अच्छा सूचक है। मैं थोड़ा गहरा – osager

5

तरल हास्केल, जो हास्केल के शीर्ष पर परिष्करण प्रकार जोड़ता है, ऐसा कर सकता है। ट्यूटोरियल here के सही खंड को देखें।

सबसे पहले आप अपने डेटा प्रकार को सामान्य के रूप में परिभाषित करते हैं।

module User where 

data User = User { uRank  :: Int 
       , uProgress :: Int 
       } 

अगला हम आपके प्रतिबंध को परिष्करण प्रकार के रूप में परिभाषित करते हैं। तरल हास्केल उपयोगकर्ता वाक्यविन्यास {[email protected] blah @-} के साथ एनोटेशन टिप्पणी करते हैं। हम -1, 1, 3RestrictedInt प्रकार के साथ पहले की अपनी अजीब प्रतिबंध को परिभाषित करेंगे:

{[email protected] type RestrictedInt = {v : Int | v == -1 || v == 1 || v == 3} @-} 

है, RestrictedInt एक Int वह यह है कि या तो -1, 1, या 3 है। ध्यान दें कि आप आसानी से श्रेणियां और अन्य प्रतिबंध लिख सकते हैं, इसे वैध मूल्यों के बारे में कुछ दिमागी गणना नहीं होनी चाहिए।

हम अब फिर से परिभाषित इस प्रतिबंधित पूर्णांक प्रकार का उपयोग हमारे शोधन भाषा में अपने डेटा प्रकार:

{[email protected] data User = User { uRank  :: RestrictedInt 
        , uProgress :: Int } 
    @-} 

यह अपनी परिभाषा के लिए, लेकिन सिर्फ Int के बजाय प्रतिबंध प्रकार के साथ इसी तरह की है। हम एक प्रकार के उपनाम रखने के बजाय प्रतिबंध को रेखांकित कर सकते थे लेकिन फिर आपके User डेटा प्रकार बहुत अपठनीय होगा।

आप अच्छे संस्कार परिभाषित कर सकते हैं और liquid उपकरण शिकायत नहीं होगा:

goodValue :: User 
goodValue = User 1 12 

लेकिन बुरी मूल्यों एक त्रुटि में परिणाम: या

badValue :: User 
badValue = User 10 12 

$ liquid so.hs (अपने संपादक के एकीकरण, अगर आपको लगता है कि है सेटअप) उत्पन्न करता है:

**** RESULT: UNSAFE ************************************************************ 


so.hs:16:12-18: Error: Liquid Type Mismatch 

16 | badValue = User 10 12 
       ^^^^^^^ 

    Inferred type 
    VV : {VV : GHC.Types.Int | VV == ?a} 

    not a subtype of Required type 
    VV : {VV : GHC.Types.Int | VV == (-1) 
           || (VV == 1 
            || VV == 3)} 

    In Context 
    ?a := {?a : GHC.Types.Int | ?a == (10 : int)} 
संबंधित मुद्दे