2013-01-05 20 views
5

क्या एक ऐसा फ़ंक्शन परिभाषित करना संभव है जो डेटा प्रकार और माप की इकाई दोनों पर सामान्य हो? उदाहरण के लिए, मैं करना चाहता हूँ क्या होगा, लेकिन संकलन नहीं है (हालांकि यह होगा उपाय की इकाइयों के बिना भी नहीं है, लेकिन मेरा मानना ​​है कि मैं बता देते हैं मैं क्या करना चाहते हैं क्या):एफ # सामान्य प्रकारों पर जेनेरिक इकाइयां

let inline dropUnit (x : 'a<_>) = x :> typeof(a) 

विचार यहां यह है कि मैंने माप की कुछ इकाइयों को परिभाषित किया है, उदाहरण के लिए "किलो" और "एल" और एक dicriminated संघ:

type Unit = 
    | Weight of float<kg> 
    | Volume of float <l> 

और मैं की तरह कुछ करना चाहते हैं:

let isValidUnitValue myUnit = 
    match myUnit with 
     | Weight(x) -> (dropUnit x) > 0. 
     | Volume(x) -> (dropUnit x) > 0. 

मुझे पता है कि इस विशेष मामले के लिए मैं सिर्फ इस्तेमाल कर सकते हैं कर रहा हूँ

let dropUnit (x : float<_>) = (float) x 

लेकिन मैंने ऊपर लिखते समय सामान्य मामले के बारे में सोचना शुरू कर दिया।

+0

एक अन्य विकल्प नकली इंटरफ़ेस में इनलाइन फ़ंक्शन का उपयोग करना है। –

उत्तर

7

अपने विशिष्ट प्रश्न के लिए कैसे अपने isValidUnitValue समारोह लिखने के लिए, जवाब है:

let inline isValidUnitValue myUnit = myUnit > LanguagePrimitives.GenericZero 

तो तुम एक साथ भेदभाव संघ परिभाषित करने की जरूरत नहीं है।

मूल प्रश्न के संबंध में क्या एक ऐसा फ़ंक्शन परिभाषित करना संभव है जो डेटा प्रकार से अधिक सामान्य और dropUnit जैसे माप की इकाई है, तो संक्षिप्त उत्तर नहीं है। यदि ऐसा फ़ंक्शन मौजूद है तो इसमें 'a<'b> -> 'a जैसे हस्ताक्षर होंगे और इसका प्रतिनिधित्व करने के लिए प्रकार प्रणाली को उच्च प्रकार लागू करना चाहिए।

लेकिन वहाँ अधिभार और इनलाइन उपयोग करने चाल हैं:

1) भार के उपयोग (एक ला सी #)

type UnitDropper = 
    static member drop (x:sbyte<_> ) = sbyte x 
    static member drop (x:int16<_> ) = int16 x 
    static member drop (x:int<_> ) = int  x 
    static member drop (x:int64<_> ) = int64 x 
    static member drop (x:decimal<_>) = decimal x 
    static member drop (x:float32<_>) = float32 x 
    static member drop (x:float<_> ) = float x 

[<Measure>] type m 
let x = UnitDropper.drop 2<m> + 3 

लेकिन यह वास्तव में एक सामान्य समारोह नहीं है, आप कुछ सामान्य पर नहीं लिख सकते हैं इसके ऊपर

> let inline dropUnitAndAdd3 x = UnitDropper.drop x + 3 ;; 
-> error FS0041: A unique overload for method 'drop' could not be determined ... 


2) इनलाइन का उपयोग करना, एक आम चाल पुन: लिखने जाता है:

let inline retype (x:'a) : 'b = (# "" x : 'b #) 

[<Measure>] type m 
let x = retype 2<m> + 3 
let inline dropUnitAndAdd3 x = retype x + 3 

समस्या यह है कि retype बहुत सामान्य है, यह आप लिखते हैं की अनुमति देगा:

let y = retype 2.0<m> + 3 

जो संकलित करता है लेकिन रन-टाइम पर विफल हो जाएगा।


3) दोनों भार के और इनलाइन का उपयोग करना: इस चाल उपयोग के द्वारा दोनों मुद्दों एक मध्यवर्ती प्रकार के माध्यम से अधिक भार का समाधान होगा, इस तरह से आप प्राप्त दोनों संकलन समय चेक और आप सामान्य कार्यों को परिभाषित करने में सक्षम होंगे :

type DropUnit = DropUnit with 
    static member ($) (DropUnit, x:sbyte<_> ) = sbyte x 
    static member ($) (DropUnit, x:int16<_> ) = int16 x 
    static member ($) (DropUnit, x:int<_> ) = int  x 
    static member ($) (DropUnit, x:int64<_> ) = int64 x 
    static member ($) (DropUnit, x:decimal<_>) = decimal x 
    static member ($) (DropUnit, x:float32<_>) = float32 x 
    static member ($) (DropUnit, x:float<_> ) = float x 

let inline dropUnit x = DropUnit $ x 

[<Measure>] type m 
let x = dropUnit 2<m> + 3 
let inline dropUnitAndAdd3 x = dropUnit x + 3 
let y = dropUnit 2.0<m> + 3 //fails at compile-time 

अंतिम पंक्ति में आप एक संकलन समय त्रुटि मिलेगी: FS0001: The type 'int' does not match the type 'float'

इस दृष्टिकोण का एक और लाभ यह है कि आप एक स्थिर मुझे परिभाषित करते हुए नए प्रकार के साथ बाद में इसे विस्तार कर सकते हैं है आपकी प्रकार की परिभाषा में mber ($) इस तरह:

type MyNumericType<[<Measure 'U>]> = 
    ... 
    static member dropUoM (x:MyNumericType<_>) : MyNumericType = ... 
    static member ($) (DropUnit, x:MyNumericType<_>) = MyNumericType.dropUoM(x) 
+0

महान उत्तर, धन्यवाद! – Bram

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