नल के बजाय शायद प्रकार वाली भाषाएं किनारे की स्थितियों को कैसे संभालती हैं?


53

एरिक लिपर्ट ने अपनी चर्चाnullMaybe<T> में एक बहुत ही दिलचस्प बात कही कि C # एक प्रकार के बजाय # का उपयोग क्यों करता है :

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

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


मुझे लगता है कि अगर हो सकता है कि यह भाषा का हिस्सा है, तो हो सकता है कि इसे आंतरिक रूप से एक अशक्त सूचक के माध्यम से लागू किया जाए और यह सिंटैक्टिक शुगर है। लेकिन मुझे नहीं लगता कि कोई भी भाषा वास्तव में ऐसा करती है।
पणजी

1
@ अपंजी: सीलोन प्रवाह-संवेदनशील टाइपिंग का उपयोग करता है ताकि Type?(शायद) और Type(शून्य नहीं ) के
लुकास ईडर

1
@RobertHarvey क्या स्टैक एक्सचेंज में "अच्छा सवाल" बटन नहीं है?
user253751

2
@panzi यह एक अच्छा और मान्य अनुकूलन है, लेकिन यह इस समस्या को हल करने में मदद नहीं करता है: जब कोई चीज़ नहीं होती है Maybe T, तो यह नहीं होनी चाहिए Noneऔर इसलिए आप इसके भंडारण को शून्य पॉइंटर के लिए प्रारंभ नहीं कर सकते हैं।

@ मिनीबिस: मैंने पहले ही इसे धकेल दिया। हमें यहाँ कुछ अच्छे प्रश्न मिलते हैं; मुझे लगा कि यह एक टिप्पणी के योग्य है।
रॉबर्ट हार्वे

जवाबों:


45

यह उद्धरण एक समस्या की ओर इशारा करता है जो तब होता है जब पहचानकर्ताओं की घोषणा और असाइनमेंट (यहां: उदाहरण के सदस्य) एक दूसरे से अलग होते हैं । एक त्वरित स्यूडोकोड स्केच के रूप में:

class Broken {
    val foo: Foo  // where Foo and Bar are non-nullable reference types
    val bar: Bar

    Broken() {
        foo = new Foo()
        throw new Exception()
        // this code is never reached, so "bar" is not assigned
        bar = new Bar()
    }

    ~Broken() {
        foo.cleanup()
        bar.cleanup()
    }
}

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

नल के साथ, विध्वंसक परीक्षण कर सकता है कि क्या एक चर की तरह असाइन किया गया था if (foo != null) foo.cleanup()। नल के बिना, वस्तु अब अपरिभाषित स्थिति में है - का मूल्य क्या है bar?

हालाँकि, यह समस्या तीन पहलुओं के संयोजन के कारण मौजूद है :

  • nullसदस्य चरों के लिए डिफ़ॉल्ट मानों की अनुपस्थिति जैसे या गारंटीकृत आरंभीकरण।
  • घोषणा और असाइनमेंट के बीच का अंतर। वेरिएबल्स को तुरंत सौंपा जाना (जैसे कि एक letकथन के रूप में कार्यात्मक भाषाओं में देखा गया) एक आसान है गारंटी गारंटीकृत आरंभीकरण - लेकिन अन्य तरीकों से भाषा को प्रतिबंधित करता है।
  • विध्वंसक का विशिष्ट स्वाद एक विधि के रूप में जिसे भाषा रनटाइम कहा जाता है।

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

// the body of the class *is* the constructor
class Working() {
    val foo: Foo = new Foo()
    FINALIZE { foo.cleanup() }  // block is registered to run when object is destroyed

    throw new Exception()

    // the below code is never reached, so
    //  1. the "bar" variable never enters the scope
    //  2. the second finalizer block is never registered.
    val bar: Bar = new Bar()
    FINALIZE { bar.cleanup() }  // block is registered to run when object is destroyed
}

इसलिए अशक्त की अनुपस्थिति के साथ कोई समस्या नहीं है, लेकिन संयोजन के साथ अशक्त अनुपस्थिति के साथ अन्य विशेषताओं का एक सेट है।

दिलचस्प सवाल अब यह है कि C # ने एक डिज़ाइन क्यों चुना लेकिन दूसरे को नहीं। यहाँ, उद्धरण का संदर्भ C # भाषा में एक अशक्त के लिए कई अन्य तर्कों को सूचीबद्ध करता है, जिन्हें ज्यादातर "परिचित और अनुकूलता" के रूप में संक्षेपित किया जा सकता है - और वे अच्छे कारण हैं।


एक और कारण भी है कि फ़ाइनलीज़र को एस से निपटना पड़ता है null: संदर्भ चक्र की संभावना के कारण अंतिम रूप देने के आदेश की गारंटी नहीं है। लेकिन मुझे लगता है कि आपका FINALIZEडिज़ाइन भी हल करता है: यदि fooपहले ही अंतिम रूप दे दिया गया है, तो इसका FINALIZEखंड बस नहीं चलेगा।
०४

14

आपके द्वारा किसी भी अन्य डेटा की गारंटी देने के तरीके मान्य स्थिति में हैं।

कोई शब्दार्थ को नियंत्रित कर सकता है और प्रवाह को नियंत्रित कर सकता है ताकि आप इसके लिए पूरी तरह से मूल्य बनाए बिना किसी प्रकार का एक चर / क्षेत्र न रख सकें। किसी ऑब्जेक्ट को बनाने और उसके फ़ील्ड में "प्रारंभिक" मान असाइन करने देने के बजाय, आप केवल एक बार ही उसके सभी फ़ील्ड के लिए मान निर्दिष्ट करके ऑब्जेक्ट बना सकते हैं। एक वैरिएबल घोषित करने और फिर एक प्रारंभिक मूल्य प्रदान करने के बजाय, आप केवल एक इनिशियलाइज़ेशन के साथ एक वैरिएबल का परिचय दे सकते हैं।

उदाहरण के लिए, रस्ट में आप Point { x: 1, y: 2 }एक कंस्ट्रक्टर लिखने के बजाय स्ट्रक्चर टाइप का ऑब्जेक्ट बनाते हैं self.x = 1; self.y = 2;। बेशक, यह आपके मन में भाषा की शैली के साथ टकरा सकता है।

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

Object o;
try {
    call_can_throw();
    o = new Object();
} catch {}
use(o);

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


7

यहाँ हैस्केल यह कैसे करता है: (लिपस्केल के कथनों से बिलकुल भी अंतर नहीं है क्योंकि हास्केल ऑब्जेक्ट-ओरिएंटेड भाषा नहीं है)।

चेतावनी: आगे एक गंभीर हास्केल फैनबॉय से लंबे घुमावदार जवाब।

टी एल; डॉ

इस उदाहरण से पता चलता है कि हस्केल सी # से कितना अलग है। एक निर्माणकर्ता को संरचना निर्माण के रसद को सौंपने के बजाय, इसे आसपास के कोड में संभाला जाना चाहिए। Nothingफसल के लिए एक शून्य (या हास्केल) मूल्य मूल्य के लिए कोई रास्ता नहीं है जहां हम एक गैर-शून्य मूल्य की उम्मीद कर रहे हैं क्योंकि शून्य मान केवल विशेष आवरण प्रकारों के भीतर हो सकते हैं जिन्हें बुलाया के Maybeसाथ विनिमेय नहीं है / सीधे, नियमित रूप से परिवर्तनीय के लिए, गैर- अशक्त प्रकार के। इसे में लपेटकर अशक्त किए गए मान का उपयोग करने के लिए Maybe, हमें पहले पैटर्न मिलान का उपयोग करके मूल्य निकालना होगा, जो हमें नियंत्रण प्रवाह को एक शाखा में मोड़ने के लिए मजबूर करता है, जहां हम निश्चित रूप से जानते हैं कि हमारे पास एक गैर-शून्य मान है।

इसलिए:

क्या हम हमेशा यह जान सकते हैं कि गैर-अशोभनीय संदर्भ किसी भी परिस्थिति में कभी भी अमान्य नहीं होगा?

हाँ। Intऔर Maybe Intदो पूरी तरह से अलग प्रकार हैं। Nothingसादे में खोजना Intस्ट्रिंग "मछली" को खोजने के लिए तुलनीय होगा Int32

संदर्भ प्रकार के गैर-अशक्त क्षेत्र के साथ किसी वस्तु के निर्माता के बारे में क्या?

कोई मुद्दा नहीं: हास्केल में मूल्य निर्माता कुछ भी नहीं कर सकते हैं, लेकिन उनके द्वारा दिए गए मूल्यों को लें और उन्हें एक साथ रखें। कंस्ट्रक्टर को कॉल करने से पहले सभी आरंभिक तर्क दिए जाते हैं।

ऐसे ऑब्जेक्ट के अंतिम रूप में क्या है, जहां ऑब्जेक्ट को अंतिम रूप दिया गया है क्योंकि संदर्भ में भरने वाला कोड एक अपवाद फेंक दिया था?

हास्केल में कोई फाइनल नहीं है, इसलिए मैं वास्तव में इसे संबोधित नहीं कर सकता। हालाँकि मेरी पहली प्रतिक्रिया अभी भी है।

पूर्ण उत्तर :

हास्केल में कोई अशक्त नहीं है और अशक्त Maybeका प्रतिनिधित्व करने के लिए डेटा प्रकार का उपयोग करता है । हो सकता है कि इस तरह परिभाषित एक अल्गब्राटिक डेटा प्रकार हो :

data Maybe a = Just a | Nothing

हास्केल से अपरिचित आप में से उन लोगों के लिए, "ए Maybeया तो ए Nothingया Just a" है। विशेष रूप से:

  • Maybeहै प्रकार निर्माता : यह एक सामान्य वर्ग (जहां के रूप में की (गलत) सोचा जा सकता है aप्रकार चर है)। C # सादृश्य है class Maybe<a>{}
  • Justएक वैल्यू कंस्ट्रक्टर है : यह एक ऐसा फंक्शन है जो टाइप का एक तर्क लेता है aऔर उस वैल्यू को टाइप Maybe aकरता है जिसमें वैल्यू होती है। तो कोड x = Just 17के अनुरूप है int? x = 17;
  • Nothingएक और मूल्य निर्माता है, लेकिन यह कोई तर्क नहीं लेता है और Maybeलौटे का "कुछ भी नहीं" के अलावा कोई मूल्य नहीं है। x = Nothingके अनुरूप है int? x = null;(यह मानकर कि हम aहास्केल में होने के लिए विवश हैं Int, जिसे लिखकर किया जा सकता है x = Nothing :: Maybe Int)।

अब जब Maybeप्रकार की मूल बातें खत्म हो गई हैं, तो हास्केल ओपी के प्रश्न में चर्चा किए गए मुद्दों से कैसे बचते हैं?

खैर, हास्केल वास्तव में अब तक चर्चा की गई अधिकांश भाषाओं से अलग है, इसलिए मैं कुछ बुनियादी भाषा सिद्धांतों की व्याख्या करके शुरू करूंगा।

सबसे पहले, हास्केल में, सब कुछ अपरिवर्तनीय है । सब कुछ। नाम मानों को संदर्भित करते हैं, स्मृति स्थानों को नहीं जहां मूल्यों को संग्रहीत किया जा सकता है (यह अकेले बग उन्मूलन का एक बड़ा स्रोत है)। सी #, जहां चर घोषणा और काम दो अलग-अलग संचालन, हास्केल मूल्यों में उनके मूल्य को परिभाषित द्वारा बनाई गई हैं कर रहे हैं के विपरीत (जैसे x = 15, y = "quux", z = Nothing), कभी नहीं बदल सकता है। इसलिए, जैसे कोड:

ReferenceType x;

हास्केल में संभव नहीं है। मानों को शुरू करने में कोई समस्या नहीं है nullक्योंकि सब कुछ स्पष्ट रूप से एक मूल्य के लिए प्रारंभिक होना चाहिए ताकि यह अस्तित्व में हो।

दूसरे, हास्केल एक वस्तु-उन्मुख भाषा नहीं है : यह विशुद्ध रूप से कार्यात्मक भाषा है, इसलिए शब्द के सख्त अर्थों में कोई वस्तु नहीं है। इसके बजाय, बस कार्य (मूल्य निर्माता) हैं जो अपने तर्क लेते हैं और एक समामेलित संरचना लौटाते हैं।

अगला, कोई अनिवार्य शैली कोड नहीं है। इसके द्वारा, मेरा मतलब है कि अधिकांश भाषाएँ इस तरह से एक पैटर्न का अनुसरण करती हैं:

do thing 1
add thing 2 to thing 3
do thing 4
if thing 5:
    do thing 6
return thing 7

कार्यक्रम व्यवहार को निर्देशों की एक श्रृंखला के रूप में व्यक्त किया जाता है। ऑब्जेक्ट-ओरिएंटेड भाषाओं में, क्लास और फंक्शन की घोषणाएं भी प्रोग्राम फ्लो में एक बड़ी भूमिका निभाती हैं, लेकिन सार है, प्रोग्राम के निष्पादन का "मांस" निष्पादित होने के लिए निर्देशों की एक श्रृंखला का रूप लेता है।

हास्केल में, यह संभव नहीं है। इसके बजाय, कार्यक्रम का प्रवाह पूरी तरह से कार्यों के जंजीरों से तय होता है। यहां तक ​​कि ऑपरेटर doको अनाम कार्यों को पारित करने के लिए अनिवार्य-दिखने वाली नोटबंदी केवल वाक्यात्मक चीनी है >>=। सभी कार्य इस प्रकार हैं:

<optional explicit type signature>
functionName arg1 arg2 ... argn = body-expression

जहां body-expressionकुछ भी हो सकता है जो एक मूल्य का मूल्यांकन करता है। जाहिर है कि अधिक वाक्यविन्यास सुविधाएँ उपलब्ध हैं लेकिन मुख्य बिंदु बयानों के अनुक्रमों की पूर्ण अनुपस्थिति है।

अंत में, और शायद सबसे महत्वपूर्ण बात, हास्केल का प्रकार प्रणाली अविश्वसनीय रूप से सख्त है। अगर मुझे हास्केल के प्रकार प्रणाली के केंद्रीय डिजाइन दर्शन को संक्षेप में प्रस्तुत करना था, तो मैं कहूंगा: "जितना संभव हो उतना संभव संकलन समय पर गलत तरीके से करें ताकि जितना संभव हो सके रनटाइम पर गलत हो।" कोई भी अंतर्निहित रूपांतरण नहीं हैं (जो एक Intको बढ़ावा देना चाहते हैं Double? fromIntegralफ़ंक्शन का उपयोग करें )। केवल संभवत: एक अमान्य मान रनटाइम पर होता है, Prelude.undefinedजिसका उपयोग करना है (जो कि स्पष्ट रूप से बस वहां होना है और निकालना असंभव है )।

इस सब को ध्यान में रखते हुए, आइए हम अमोन के "टूटे" उदाहरण को देखें और हास्केल में इस कोड को फिर से व्यक्त करने का प्रयास करें। सबसे पहले, डेटा घोषणा (नामित क्षेत्रों के लिए रिकॉर्ड सिंटैक्स का उपयोग करके):

data NotSoBroken = NotSoBroken {foo :: Foo, bar :: Bar } 

( fooऔर barवास्तविक क्षेत्रों के बजाय यहां अनाम क्षेत्रों में वास्तव में एक्सेसर फ़ंक्शन हैं, लेकिन हम इस विवरण को अनदेखा कर सकते हैं)।

NotSoBrokenमूल्य निर्माता एक लेने के अलावा अन्य कोई भी कार्रवाई करने में असमर्थ है Fooऔर एक Bar(जो व्यर्थ नहीं कर रहे हैं) और एक बनाने NotSoBrokenउनमें से बाहर। अनिवार्य कोड डालने या खेतों को मैन्युअल रूप से असाइन करने के लिए कोई जगह नहीं है। सभी आरंभीकरण तर्क कहीं और होने चाहिए, सबसे अधिक संभावना एक समर्पित कारखाने के कार्य में।

उदाहरण में, Brokenहमेशा निर्माण विफल रहता है। समान तरीके से NotSoBrokenवैल्यू कंस्ट्रक्टर को तोड़ने का कोई तरीका नहीं है (कोड लिखने के लिए बस कहीं नहीं है), लेकिन हम एक कारखाना फ़ंक्शन बना सकते हैं जो समान रूप से दोषपूर्ण है।

makeNotSoBroken :: Foo -> Bar -> Maybe NotSoBroken
makeNotSoBroken foo bar = Nothing

(पहली पंक्ति एक प्रकार का हस्ताक्षर घोषणा है: makeNotSoBrokenएक Fooऔर एक Barतर्क के रूप में लेता है और पैदा करता है Maybe NotSoBroken)।

वापसी प्रकार होना चाहिए Maybe NotSoBrokenऔर केवल NotSoBrokenइसलिए नहीं कि हमने इसे मूल्यांकन करने के लिए कहा था Nothing, जो इसके लिए एक मूल्य निर्माता है Maybe। यदि हम कुछ अलग लिखते हैं तो प्रकार केवल पंक्तिबद्ध नहीं होंगे।

बिल्कुल निरर्थक होने के बावजूद, यह फ़ंक्शन अपने वास्तविक उद्देश्य को भी पूरा नहीं करता है, जैसा कि हम देखेंगे जब हम इसका उपयोग करने का प्रयास करेंगे। आइए एक फ़ंक्शन बनाएं जिसे एक तर्क के रूप में useNotSoBrokenउम्मीद है NotSoBroken:

useNotSoBroken :: NotSoBroken -> Whatever

( तर्क के रूप में useNotSoBrokenस्वीकार करता है NotSoBrokenऔर पैदा करता है Whatever)।

और इसका इस्तेमाल ऐसे करें:

useNotSoBroken (makeNotSoBroken)

अधिकांश भाषा में, इस तरह के व्यवहार से अशक्त सूचक अपवाद हो सकता है। हास्केल में, प्रकार मेल नहीं खाते: makeNotSoBrokenरिटर्न ए Maybe NotSoBroken, लेकिन useNotSoBrokenउम्मीद है कि ए NotSoBroken। ये प्रकार विनिमेय नहीं हैं, और कोड संकलित करने में विफल रहता है।

इसके आस-पास जाने के लिए, हम मूल्य caseके ढांचे के आधार पर शाखा के लिए एक बयान का उपयोग कर सकते हैं Maybe( पैटर्न मिलान नामक एक सुविधा का उपयोग करके ):

case makeNotSoBroken of
    Nothing  -> --handle situation here
    (Just x) -> useNotSoBroken x

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

  • सबसे पहले, makeNotSoBrokenमूल्यांकन किया जाता है, जो प्रकार के मूल्य का उत्पादन करने की गारंटी है Maybe NotSoBroken
  • caseबयान इस मूल्य की संरचना निरीक्षण करता है।
  • यदि मान है Nothing, तो "हैंडल स्थिति यहां" कोड का मूल्यांकन किया जाता है।
  • यदि इसके बजाय मान किसी Justमान के विरुद्ध मेल खाता है , तो दूसरी शाखा निष्पादित होती है। ध्यान दें कि कैसे मिलान खंड एक साथ मूल्य को एक Justनिर्माण के रूप में पहचानता है और इसके आंतरिक NotSoBrokenक्षेत्र को एक नाम (इस मामले में x) में बांधता है । xफिर सामान्य NotSoBrokenमूल्य की तरह उपयोग किया जा सकता है।

इसलिए, पैटर्न मिलान प्रकार की सुरक्षा को लागू करने के लिए एक शक्तिशाली सुविधा प्रदान करता है, क्योंकि ऑब्जेक्ट की संरचना को नियंत्रण के शाखा में अविभाज्य रूप से बांधा गया है।

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


TL, DR शीर्ष पर होना चाहिए :)
andrew.fox

@ andrew.fox अच्छा बिंदु। मैं संपादित करूँगा।
अप्रमाणनारदापनफिश

0

मुझे लगता है कि आपका उद्धरण एक स्ट्रॉ मैन तर्क है।

आधुनिक भाषाएं आज (C # सहित) आपको गारंटी देती हैं कि कंस्ट्रक्टर या तो पूरी तरह से पूरा हो गया है या नहीं।

यदि कंस्ट्रक्टर में कोई अपवाद है और ऑब्जेक्ट को आंशिक रूप से असंगठित छोड़ दिया जाता है, तो अनइंस्टॉल किए गए राज्य के लिए nullया होने Maybe::noneसे विध्वंसक कोड में कोई वास्तविक अंतर नहीं होता है।

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

Btw: C # में, nullमान बहुत अधिक के बराबर है Maybe::none। आप nullकेवल चर और ऑब्जेक्ट सदस्यों को बता सकते हैं कि एक प्रकार के स्तर को अशक्त घोषित किया गया है :

String? nullableString = getOptionalString();
Nullable<String> maybe = nullableString; // This is equivalent

यह निम्नलिखित स्निपेट से भिन्न नहीं है:

Maybe<String> optionalString = getOptionalString();

इसलिए निष्कर्ष में, मैं यह नहीं देखता कि किसी भी प्रकार के विपरीत अशक्तता कैसे है Maybe। मैं यह भी सुझाव देना चाहूंगा कि C # ने अपने Maybeप्रकार में छींक दी है और इसे बुलाया है Nullable<T>

विस्तार के तरीकों के साथ, अशक्त की साफ- सुथरी संरचना को प्राप्त करना और भी आसान है :

Resource? resource = initializationThatMayFail();
...
resource.ifExists( Resource r -> r.cleanup() );

2
इसका क्या मतलब है, "निर्माता या तो पूरी तरह से पूरा करता है या यह नहीं करता है"? उदाहरण के लिए, जावा में कंस्ट्रक्टर में (नॉन-फ़ाइनल) फ़ील्ड की आरंभीकरण को डेटा की दौड़ से संरक्षित नहीं किया जाता है - क्या यह पूरी तरह से योग्य है या नहीं?
कुटकी

@gnat: "जावा में उदाहरण के लिए, कंस्ट्रक्टर में (गैर-अंतिम) फ़ील्ड को आरम्भ करने का क्या मतलब है, डेटा रेस से संरक्षित नहीं है"। जब तक आप कई थ्रेड्स को शामिल करते हुए शानदार ढंग से जटिल नहीं करते हैं, एक निर्माणकर्ता के अंदर दौड़ की स्थिति की संभावना असंभव के पास (या होनी चाहिए) है। आप ऑब्जेक्ट कंस्ट्रक्टर के अलावा किसी अनियंत्रित ऑब्जेक्ट के क्षेत्र तक नहीं पहुँच सकते। और यदि निर्माण विफल रहता है, तो आपके पास ऑब्जेक्ट का संदर्भ नहीं है।
रोलैंड टेप

nullहर प्रकार के निहित सदस्य के रूप में बड़ा अंतर Maybe<T>यह है कि Maybe<T>आपके पास बस हो सकता है T, जिसका कोई डिफ़ॉल्ट मूल्य नहीं है।
svick

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

@ शविक: सी # में (जो ओपी द्वारा प्रश्न में भाषा थी), nullहर प्रकार का निहित सदस्य नहीं है। के लिए nulllebal मूल्य होने के लिए आपको प्रकार स्पष्ट रूप से व्यर्थ होने के लिए है, जो एक बनाता है परिभाषित करने की जरूरत T?(के लिए वाक्यविन्यास चीनी Nullable<T>) अनिवार्य रूप से करने के लिए बराबर Maybe<T>
रोलैंड टीपीपी

-3

सी ++ कंस्ट्रक्टर बॉडी से पहले होने वाले इनिशियलाइजर तक पहुंच बनाकर करता है। सी # कंस्ट्रक्टर बॉडी से पहले डिफॉल्ट इनिशियलाइज़र चलाता है, यह मोटे तौर पर 0 को सब कुछ असाइन करता है, floats0.0 boolsहो जाता है, गलत हो जाता है, रेफरेंस अशक्त हो जाते हैं, आदि। C ++ में आप यह सुनिश्चित कर सकते हैं कि यह एक नॉन-न्यूलर रेफरेंस टाइप सुनिश्चित करने के लिए एक अलग इनीशलाइज़र चलाए। ।

class Foo { Foo(int i) { throw new Exception("Never finishes"); }
class Bar { Bar(string s) { } }

class Broken
{
    val foo: Foo  // where Foo and Bar are non-nullable reference types
    val bar: Bar

    Broken() :
        foo = new Foo(123),// roughly causes a "goto destroy_foo;"
        bar = new Bar("never executes") { }

    // This destructory-function never runs because the constructor never completed
    ~Broken() 
    // This is made-up syntax:
    // : 
    // destroy_bar:
    // bar.~Bar();
    // destroy_foo:
    // foo.~Foo();
    {
    }
}

2
सवाल शायद टाइप्स वाली भाषाओं के बारे में था
gnat

3
" संदर्भ निरर्थक हो जाते हैं " - सवाल का पूरा आधार यह है कि हमारे पास नहीं है null, और मूल्य की अनुपस्थिति को इंगित करने का एकमात्र तरीका एक Maybeप्रकार (जिसे भी जाना जाता है Option) का उपयोग करना है, जो AFAIK C ++ में नहीं है मानक पुस्तकालय। नल की अनुपस्थिति हमें यह गारंटी देने की अनुमति देती है कि एक क्षेत्र हमेशा टाइप सिस्टम की संपत्ति के रूप में मान्य होगा । यह मैन्युअल रूप से सुनिश्चित करने की तुलना में एक मजबूत गारंटी है कि कोई कोड पथ मौजूद नहीं है जहां एक चर अभी भी हो सकता है null
आमोन

हालांकि c ++ में मूल रूप से मूल रूप से नहीं हो सकता है, शायद स्पष्ट रूप से std :: साझा_ptr <T> जैसी चीजें पर्याप्त हैं, जो मुझे लगता है कि यह अभी भी प्रासंगिक है कि c ++ उस मामले को संभालता है जहां चर का आरंभीकरण कंस्ट्रक्टर के "दायरे से बाहर" हो सकता है, और वास्तव में संदर्भ प्रकारों (&) के लिए आवश्यक है, क्योंकि वे शून्य नहीं हो सकते।
फ्राईग्यूय
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.