2010-10-11 7 views
7

समस्या का सारांशएफ # पैटर्न मैचों में सामान्य सुपर प्रकारों में पूर्ण रूप से कैसे परिवर्तित करें?

फिलहाल जब च # का उपयोग कर मैंने स्पष्ट रूप से आदेश टाइप करने के लिए सही ढंग से जाँच पैटर्न मिलान भाव प्राप्त करने के लिए अपनी तरह का पेरेंट प्रकार के लिए एक मूल्य के लिए मजबूर करना होगा। मैं आदर्श रूप से करने के एक साफ तरीके की तरह होगा।

उदाहरण

मान लीजिए मैं कुछ वर्ग पदानुक्रम है:

let bar (i:int) : Foo = 
    match i with 
     | 1 -> B "one" 
     | _ -> A i 
:

type Foo() = 
    abstract member Value : unit -> string 

type A (i:int) = 
    inherit Foo() 
     override this.Value() = i.ToString() 

type B (s:string) = 
    inherit Foo() 
     override this.Value() = s 

आदर्श रूप में, और सामान्य रूप से में कुछ प्रोग्रामिंग भाषाओं में, मैं निम्नलिखित के बराबर लिखते थे

हालांकि यह सही तरीके से जांचने में विफल रहता है, मुझे त्रुटि दे रहा है, "इस अभिव्यक्ति से फू टाइप करने की उम्मीद थी लेकिन उसे ई प्रकार बी है "। मुझे समझ में नहीं आता कि क्यों संकलक के पास मिलान अभिव्यक्ति के लिए एक सामान्य सुपर प्रकार का अनुमान लगाने के लिए पर्याप्त जानकारी नहीं है और फिर जांचें कि सामान्य सुपर प्रकार 'फू' है।

वर्तमान में मैं पैटर्न मैच में हर मामले के लिए एक स्पष्ट बलात्कार प्रदान करने के लिए मजबूर कर रहा हूँ:

let bar2 (i:int) : Foo = 
    match i with 
     | 1 -> (B "one") :> Foo 
     | _ -> (A i) :> Foo 

मैं इस से बचने के लिए चाहते हैं।

इसके अलावा नोट्स

  • अंतर्ज्ञान चलता है कि यह एक अधिक सामान्य मुद्दे का परिणाम है। मैंने सोचा होगा कि पैटर्न मिलान के रूप में कुछ सामान्य है, या यदि बयान जो एक ही संपत्ति को प्रदर्शित करता है, तो सामान्य सुपर प्रकारों के लिए एक प्रकार की जांच नियम होगा।
  • किसी से भी सुझाव देने से पहले - मैं सराहना करता हूं कि यदि ए या बी ऑब्जेक्ट एक्सप्रेशन थे तो यह काम करेगा, लेकिन मेरा असली उदाहरण सी # कक्षाओं के उदाहरण बना रहा है जहां वे सामान्य कक्षाएं हैं।
  • क्या मेरे लिए अलग-अलग प्रकार के रूपांतरित करने के लिए कार्यों की घोषणा करने का कोई तरीका है, उदाहरण के लिए स्कैला है, इसलिए मैं मॉड्यूल के लिए स्वचालित रूपांतरण लागू कर सकता हूं जहां मैं इस पीढ़ी को कर रहा हूं?

इस मामले पर किसी भी मदद के लिए धन्यवाद।

उत्तर

7

मैं upcast का प्रयोग करेंगे, एक ला

[<AbstractClass>] 
type Foo() = 
    abstract member Value : unit -> string 

type A (i:int) = 
    inherit Foo() 
    override this.Value() = i.ToString() 

type B (s) = 
    inherit Foo() 
    override this.Value() = s 

let bar2 i : Foo = 
    match i with 
    | 1 -> upcast B "one" 
    | _ -> upcast A i 

तुम अब भी हर शाखा में जोड़ने के लिए है, लेकिन यह, अक्सर प्रकार के कास्टिंग के लिए बेहतर है, क्योंकि अक्सर typename 20 या 30 वर्ण लंबा की तरह है (MyNamespace.ThisThingy), जबकि upcast केवल 6 वर्ण हैं।

लेकिन, संक्षेप में, भाषा नियम किसी और चीज की अनुमति नहीं देते हैं, सभी शाखाओं के प्रकार बराबर होना चाहिए।

+2

टाइप चेकर व्युत्पन्न प्रकारों के साथ कोई शेंगेन नहीं करता है, पहला यह है कि यह प्रकार की जांच चरण को अधिक जटिल बना देगा और दूसरा यह आपको सूक्ष्म बग पेश करने की अनुमति देगा। उदाहरण के लिए, निम्न पर विचार करें: मिलान के साथ। 1 -> बी ("एक") | 2 -> "सामान" ठीक है, सिस्टम.स्ट्रिंग और बी दोनों सामान्य बेस प्रकार सिस्टम साझा करें। ऑब्जेक्ट, इसलिए उस मिलान अभिव्यक्ति का परिणाम ओबीजे प्रकार का है। 99.9% जो आप चाहते थे वह नहीं है। –

+1

सच है, हालांकि विशिष्ट उदाहरण में ओपी पूछ रहा है, उसने पहले से ही फ़ंक्शन रिटर्न प्रकार को ': Foo' के रूप में घोषित कर दिया है, इसलिए मुझे लगता है कि यह इंफ्रेंसर के लिए इसे सबसे महान-निचले स्तर के रूप में उपयोग करने के लिए पूरी तरह से अनुचित नहीं होगा। बाध्य या कुछ। हालांकि, मैंने इसके सभी प्रभावों के बारे में सोचा नहीं है। – Brian

+1

मुझे लगता है कि 'x:> _'' अपकास्ट x' से बेहतर पढ़ता है, लेकिन हो सकता है कि यह सिर्फ मुझे है। हालांकि, मैच की शाखाएं भी लाइन नहीं होती हैं। – kvb

5

मैंने इस सवाल को दो बार पहले देखा है, लेकिन मुझे अभी एहसास हुआ है कि इस मुद्दे को हल करने के लिए काफी दिलचस्प तरीका है (बिना किसी बड़े नकारात्मक प्रभाव के बड़े प्रभाव के बिना)।

आप एक बहुत ही सरल गणना अभिव्यक्ति का उपयोग कर सकते हैं जिसमें केवल Return सदस्य हैं। निर्माता के पास एक प्रकार पैरामीटर होगा और Return इस प्रकार के मानों की अपेक्षा करेगा। चाल यह है कि एक सदस्य को कॉल करते समय F # स्वचालित अपवाद डालता है। यहाँ घोषणा है:

type ExprBuilder<'T>() = 
    member x.Return(v:'T) = v 

let expr<'T> = ExprBuilder<'T>() 

एक सरल पैटर्न मिलान कि obj के रूप में कुछ भी देता है लिखने के लिए, आप अब लिख सकते हैं:

let foo a = expr<obj> { 
    match a with 
    | 0 -> return System.Random() 
    | _ -> return "Hello" } 

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

+0

यह एक दिलचस्प समाधान है, लेकिन मुझे अभी भी पैटर्न मिलान की प्रत्येक पंक्ति पर वाक्यविन्यास होना है। फिर भी - यह निश्चित रूप से अब तक का सबसे अच्छा समाधान है! –

+2

-1, मेरी राय में, यह "बहुत चालाक" है। – Brian

+0

@ ब्रायन: मैं इसे तारीफ के रूप में ले जाऊंगा :-), लेकिन आप सही हैं, शायद यह बहुत मुश्किल है (और मैंने अंतिम वाक्य में लिखा है) हालांकि यह हमेशा प्रोजेक्ट के प्रकार पर निर्भर करता है - क्या यह कुछ है सी #/एफ # प्लंबिंग कोड या एक जटिल एफ # प्रणाली का मुख्य हिस्सा? कुछ मामलों में, मुश्किल समाधानों की अनुमति दी जानी चाहिए :-)। और आखिरकार, _asynchronous workflows_ भी मुश्किल हैं (एक अर्थ में)। –

संबंधित मुद्दे