2016-10-25 10 views
7

सफल होता है, मुझे शायद ही कभी यह संघर्ष F # के साथ होता है, लेकिन फिर फिर से विरासत एफ # के साथ बहुत कम आम है, इसलिए शायद मैं भाग्यशाली था। या मैं स्पष्ट याद कर रहा हूँ। आम तौर पर जब संकलक एक निश्चित प्रकार को जानने के बारे में शिकायत करता है तो मैं पाइप या संरचना ऑपरेंड के क्रम को उलट देता हूं और मैं कर रहा हूं।पाइपिंग या संरचना के साथ टाइप अनुमान विफल रहता है, जहां सामान्य फ़ंक्शन कॉल

असल में, एक फ़ंक्शन कॉल दिया गया जो g(f x) के रूप में काम करता है, यह x |> f |> g या (f >> g) x के रूप में भी काम करता है। लेकिन आज ऐसा नहीं होता ...

यहाँ एक गंदा मैं क्या मतलब का सबूत अवधारणा-का-है:

module Exc = 
    open System 

    type MyExc(t) = inherit Exception(t) 

    let createExc t = new MyExc(t) 
    type Ex = Ex of exn 
    type Res = Success of string | Fail of Ex with 
     static member createRes1 t = Ex(createExc(t)) |> Fail // compiled 
     static member createRes2 t = t |> createExc |> Ex |> Fail // FS0001 
     static member createRes3 = createExc >> Ex >> Fail // FS0001 

आम तौर पर, यह काम करता है (कम से कम मेरे अनुभव में)। "असफल" फेंक वाली रेखाएं:

त्रुटि FS0001: मिस्चैच टाइप करें। एक MyExc की अपेक्षा -> 'ए लेकिन एक exn दिया -> पूर्व। प्रकार 'MyExc' प्रकार से मेल नहीं खाता 'exn'

नहीं एक बड़ी बात, मुश्किल नहीं वैकल्पिक हल के लिए, लेकिन मैं कोड का एक बहुत कुछ है, जहां रचना आसान/क्लीनर दृष्टिकोण है लिखने के लिए है करने के लिए होती हैं और मैं उपयोगिता कार्यों का एक गुच्छा लिखना नहीं चाहता जो मुझे हर जगह रखना है।

मैंने लचीला प्रकार पर देखा, क्योंकि मुझे लगता है कि यह एक विरोधाभासी समस्या है, लेकिन मुझे नहीं लगता कि मैं इसे यहां कैसे लागू कर सकता हूं। इस मूर्खतापूर्ण रखने के लिए कोई विचार?

नोट, अगर मैं पुनर्व्यवस्थित करता हूं, यानी Ex <<createExc>> Fail या पाइप-पिछड़े ऑपरेटर का उपयोग करके मैं एक अलग हिस्से पर एक ही त्रुटि के साथ समाप्त होता हूं।

उत्तर

7

एफ # कंपाइलर इस मामले में थोड़ा अनियमित रूप से व्यवहार करता है। आपके उदाहरण में, आप exn की अपेक्षा रखने वाले निर्माता के लिए MyExc प्रकार का मान पास करना चाहते हैं। किसी ऑब्जेक्ट को अपनी बेस क्लास के मान के रूप में मानना ​​एक वैध सहकर्मी है, लेकिन एफ # कंपाइलर केवल सीमित स्थानों में ऐसे सहकर्मियों को सम्मिलित करता है।

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

आपके उदाहरण में, एक भेदभावपूर्ण संघ कन्स्ट्रक्टर को मूल्य पारित करते समय आपको एक कोसर की आवश्यकता होती है। ऐसा लगता है कि यह केवल तब होता है जब सीधे संघ मामले बनाने, लेकिन यह जब एक समारोह के रूप संघ मामले का इलाज नहीं होता करता है:

// foo is a function that takes `obj` and Foo is a DU case that takes `obj` 
let foo (o:obj) = o 
type Foo = Foo of obj 

foo(System.Random()) // Coersion inserted automatically 
Foo(System.Random()) // Coersion inserted automatically 

System.Random() |> foo // Coersion inserted automatically 
System.Random() |> Foo // ..but not here! 

तो, स्थानों की सीमित सेट जहां एफ # संकलक coersions स्वचालित रूप से लागू होता है शामिल कार्यों को कॉल करने के विभिन्न तरीकों, लेकिन डीयू मामलों को बनाने का केवल सीधा तरीका है।

यह थोड़ा मजाकिया व्यवहार है - और मुझे लगता है कि जब आप |> का उपयोग करते हैं तो डीयू मामलों को सामान्य कार्यों के रूप में सामान्य कार्यों के रूप में समझने के लिए समझदारी होगी, लेकिन मुझे यकीन नहीं है कि क्या कोई तकनीकी कारण हैं इसे कठिन बनाओ।

+0

हमेशा के रूप में उत्कृष्ट और अंतर्दृष्टि, धन्यवाद Tomas। ऐसा लगता है कि हम इस समझौते में हैं कि यह निश्चित रूप से एक अजीब तरह का व्यवहार है। मैंने हमेशा सोचा (और आमतौर पर यह सही है) कि डीयू कन्स्ट्रक्टर फ़ंक्शंस की तरह व्यवहार करते हैं और मैं उन्हें हर समय श्रृंखला/लिखता हूं। डीयू सदस्यों के हस्ताक्षर को देखते हुए, वे "जैसे दिखते हैं" कार्यों। – Abel

2

आप विरासत बाधा के साथ जेनेरिक प्रकार बना सकते हैं।

open System 

type MyExc (t) = inherit Exception (t) 

let createExc t = MyExc (t) 
type Ex<'t when 't :> exn> = Ex of 't 
type Res<'t when 't :> exn> = Success of string | Fail of 't Ex with 
    static member createRes1 t = Ex (createExc t) |> Fail 
    static member createRes2 t = t |> createExc |> Ex |> Fail 
    static member createRes3 = createExc >> Ex >> Fail 
+0

हां, यह एक व्यावहारिक समाधान है (मानते हैं कि प्रकार सुलभ हैं)। मैं बेहतर समझने की उम्मीद कर रहा था कि क्यों 'एफ एक्स' सफल होता है, लेकिन 'x |> f' नहीं करता है, या उन्हें दोनों को सफल कैसे बनाया जाता है। ऑपरेटर '|>' रेखांकित है, तो निश्चित रूप से ये कॉल बराबर होना चाहिए? – Abel

3

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

उदाहरण के लिए:

type U() = inherit exn() 
type T = T of exn 

let g f x = f x 

let e = U() 
let a = T e  // works 
let b = g T e  // compile error: `e` was expected to have type `exn`, but here has type `U` 

अंतिम पंक्ति, संघ निर्माता T एक नि: शुल्क समारोह के रूप में प्रयोग किया जाता है, तो यह subtyping पैच खो देता है।

मजे की बात है पर्याप्त, यह नियमित कार्यों के लिए काम करता है (जो कि संघ कंस्ट्रक्टर्स के रूप में बाहर शुरू नहीं किया यानी):

let makeT u = T u 
let a = makeT e  // works 
let b = g makeT e // also works! 

और यह भी बिंदु से मुक्त काम करता है:

let makeT = T 
let a = makeT e  // works 
let b = g makeT e // still works! 

इस विस्तार आपके लिए एक कामकाज सुझाता है: आप केवल Ex कन्स्ट्रक्टर को एक और नाम दे सकते हैं, और पाइपिंग काम करेगा:

type Ex = Ex of exn 
let makeEx = Ex 

    static member createRes2 t = t |> createExc |> makeEx |> Fail // Should work now 
संबंधित मुद्दे