design - प्रकार सुरक्षा के संबंध में हास्केल प्रकार बनाम newtype




haskell types (3)

मुझे पता है कि नए प्रकार की तुलना अक्सर हास्केल में data की तुलना में की जाती है, लेकिन मैं तकनीकी तुलना के मुकाबले डिज़ाइन पॉइंट-ऑफ-व्यू से अधिक तुलना कर रहा हूं।

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

तो, कितनी बार, हास्केल प्रोग्रामर अन्यथा आदिम मूल्यों के लिए प्रकार भेद देने के लिए नए प्रकार का काम करते हैं? type का उपयोग उपनाम प्रस्तुत करता है और एक प्रोग्राम की पठनीयता स्पष्ट अर्थशास्त्र देता है, लेकिन मूल्यों के आकस्मिक रूप से इंटरचेंज को रोकता नहीं है। जैसा कि मैंने हैकेल सीख लिया है, मुझे पता है कि टाइप सिस्टम जितना शक्तिशाली है उतना शक्तिशाली है जितना मैंने पार किया है। इसलिए, मुझे लगता है कि यह एक प्राकृतिक और सामान्य प्रथा है, लेकिन मैंने इस प्रकाश में नए प्रकार के उपयोग के बारे में ज्यादा या कोई चर्चा नहीं देखी है।

बेशक बहुत से प्रोग्रामर चीजें अलग-अलग करते हैं, लेकिन क्या यह हैकेल में आम है?


नए प्रकार के लिए मुख्य उपयोग हैं:

  1. प्रकार के लिए वैकल्पिक उदाहरण परिभाषित करने के लिए।
  2. प्रलेखन।
  3. डेटा / प्रारूप शुद्धता आश्वासन।

मैं अभी एक आवेदन पर काम कर रहा हूं जिसमें मैं बड़े पैमाने पर नए प्रकार का उपयोग करता हूं। हास्केल में newtypes पूरी तरह से संकलित-समय अवधारणा हैं। नीचे अनचाहे के साथ, unFilename (Filename "x") एक ही कोड में "x" के रूप में संकलित। बिल्कुल शून्य रन-टाइम हिट है। data प्रकारों के साथ है। यह उपर्युक्त सूचीबद्ध लक्ष्यों को प्राप्त करने का एक बहुत अच्छा तरीका बनाता है।

-- | A file name (not a file path).
newtype Filename = Filename { unFilename :: String }
    deriving (Show,Eq)

मैं गलती से इसे फ़ाइल पथ के रूप में नहीं मानना ​​चाहता हूं। यह एक फ़ाइल पथ नहीं है। यह डेटाबेस में कहीं एक वैचारिक फ़ाइल का नाम है।

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

-- | A sanitized (safe) filename.
newtype SanitizedFilename = 
  SanitizedFilename { unSafe :: String } deriving Show

-- | Unique, sanitized filename.
newtype UniqueFilename =
  UniqueFilename { unUnique :: SanitizedFilename } deriving Show

-- | An uploaded file.
data File = File {
   file_name     :: String         -- ^ Uploaded file.
  ,file_location :: UniqueFilename -- ^ Saved location.
  ,file_type     :: String         -- ^ File type.
  } deriving (Show)

मान लीजिए मेरे पास यह फ़ंक्शन है जो अपलोड किए गए फ़ाइल से फ़ाइल नाम साफ़ करता है:

-- | Sanitize a filename for saving to upload directory.
sanitizeFilename :: String            -- ^ Arbitrary filename.
                 -> SanitizedFilename -- ^ Sanitized filename.
sanitizeFilename = SanitizedFilename . filter ok where 
  ok c = isDigit c || isLetter c || elem c "-_."

अब से मैं एक अद्वितीय फ़ाइल नाम उत्पन्न करता हूं:

-- | Generate a unique filename.
uniqueFilename :: SanitizedFilename -- ^ Sanitized filename.
               -> IO UniqueFilename -- ^ Unique filename.

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

लेकिन यह बहुत लपेटना / खोलना भी परेशान हो सकता है। लंबे समय तक, मैं इसे मूल्य के रूप में देखता हूं, विशेष रूप से मूल्य विसंगतियों से बचने के लिए। ViewPatterns कुछ हद तक मदद करते हैं:

-- | Get the form fields for a form.
formFields :: ConferenceId -> Controller [Field]
formFields (unConferenceId -> cid) = getFields where
   ... code using cid ..

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


मुझे लगता है कि टाइप भेदभाव के लिए नए प्रकार का उपयोग करना काफी आम है। कई मामलों में ऐसा इसलिए होता है क्योंकि आप विभिन्न प्रकार के वर्ग के उदाहरण देना चाहते हैं, या कार्यान्वयन को छिपाना चाहते हैं, लेकिन आकस्मिक रूपांतरणों के खिलाफ सुरक्षा करना चाहते हैं, यह भी करने का एक स्पष्ट कारण है।


सरल X = Y घोषणाओं के लिए, type दस्तावेज है; newtype प्रकार की जांच है; यही कारण है कि newtype की तुलना data

मैं अक्सर आपके द्वारा वर्णित उद्देश्य के लिए नए प्रकार का उपयोग करता हूं: यह सुनिश्चित करना कि जो कुछ संग्रहीत किया जाता है (और अक्सर छेड़छाड़) उसी तरह से होता है जैसे किसी अन्य प्रकार से किसी अन्य चीज़ से भ्रमित नहीं होता है। इस तरह यह थोड़ा और अधिक कुशल data घोषणा के रूप में काम करता है; एक दूसरे को चुनने का कोई विशेष कारण नहीं है। ध्यान दें कि जीएचसी के GeneralizedNewtypeDeriving एक्सटेंशन के साथ, आप या तो स्वचालित रूप से न्यूम जैसे कक्षाएं प्राप्त कर सकते हैं, जिससे आपके तापमान या येन को जोड़ा जा सकता है और जैसे ही आप Int एस के साथ कर सकते हैं या जो भी उनके नीचे है। हालांकि, इसके साथ थोड़ा सावधान रहना चाहता है; आमतौर पर एक तापमान दूसरे तापमान से गुणा नहीं करता है!

इस बात का विचार करने के लिए कि इन चीजों का कितनी बार उपयोग किया जाता है, एक उचित रूप से बड़ी परियोजना में मैं अभी काम कर रहा हूं, मेरे पास data लगभग 122 उपयोग, नए प्रकार के 39 उपयोग, और 96 type के type

लेकिन अनुपात, जहां तक ​​"सरल" प्रकारों का संबंध है, यह दर्शाता है कि उस प्रदर्शन के मुकाबले थोड़ा करीब है, क्योंकि उन 96 type 32 उपयोग वास्तव में फ़ंक्शन प्रकारों के लिए उपनाम हैं, जैसे कि

type PlotDataGen t = PlotSeries t -> [String]

आप यहां दो अतिरिक्त जटिलताओं को नोट करेंगे: सबसे पहले, यह वास्तव में एक साधारण प्रकार है, न केवल एक साधारण X = Y उपनाम, और दूसरा यह पैरामीटरकृत है: PlotDataGen एक प्रकार का कन्स्ट्रक्टर है जो मैं एक नया प्रकार बनाने के लिए किसी अन्य प्रकार पर लागू होता हूं, जैसे PlotDataGen (Int,Double) । जब आप इस तरह की चीज करना शुरू करते हैं, तो type अब दस्तावेज नहीं है, लेकिन वास्तव में एक फ़ंक्शन है, हालांकि डेटा स्तर की बजाय टाइप स्तर पर।

newtype का कभी-कभी उपयोग किया जाता है जहां type नहीं किया जा सकता है, जैसे कि एक पुनरावर्ती प्रकार की परिभाषा आवश्यक है, लेकिन मुझे यह काफी दुर्लभ लगता है। तो ऐसा लगता है कि, इस विशेष परियोजना पर कम से कम, मेरी "आदिम" प्रकार परिभाषाओं का लगभग 40% नया प्रकार है और 60% type एस हैं। कई प्रकार की नई परिभाषाएं प्रयुक्त होती थीं, और निश्चित रूप से आपके द्वारा वर्णित सटीक कारणों के लिए परिवर्तित की गई थीं।

तो संक्षेप में, हाँ, यह एक लगातार मुहावरे है।





type-systems