2013-08-03 5 views
8

मैं निम्नलिखित भेदभाव संघ को परिभाषित किया है:प्रकार विस्तार

let rec stringify expr = 
    match expr with 
    | Con(x) -> string x 
    | Var(x) -> string x 
    | Add(x, y) -> sprintf "(%s + %s)" (stringify x) (stringify y) 
    | Sub(x, y) -> sprintf "(%s - %s)" (stringify x) (stringify y) 
    | Mult(x, y) -> sprintf "(%s * %s)" (stringify x) (stringify y) 
    | Div(x, y) -> sprintf "(%s/%s)" (stringify x) (stringify y) 
    | Pow(x, y) -> sprintf "(%s ** %s)" (stringify x) (stringify y) 

अब मैं अपने Expr बनाना चाहते:

type Expr = 
    | Con of Num 
    | Var of Name 
    | Add of Expr * Expr 
    | Sub of Expr * Expr 
    | Mult of Expr * Expr 
    | Div of Expr * Expr 
    | Pow of Expr * Expr 

तब मैं इस प्रकार एक बहुत मुद्रण समारोह बनाया इस फ़ंक्शन का उपयोग अपने ToString() विधि के लिए करें। उदाहरण के लिए:

type Expr = 
    | Con of Num 
    | Var of Name 
    | Add of Expr * Expr 
    | Sub of Expr * Expr 
    | Mult of Expr * Expr 
    | Div of Expr * Expr 
    | Pow of Expr * Expr 
    override this.ToString() = stringify this 

लेकिन मैं ऐसा नहीं कर सकते, क्योंकि stringify अभी तक परिभाषित नहीं है। जवाब Stringify को Expr के सदस्य के रूप में परिभाषित करना है, लेकिन मैं समय के साथ बढ़ते रहने के लिए इस विशेष विधि के साथ अपनी प्रारंभिक प्रकार की घोषणा को प्रदूषित नहीं करना चाहता हूं। इसलिए, मैंने एक अमूर्त विधि का उपयोग करने का निर्णय लिया जिसे मैं फ़ाइल में intrinsic type extension के साथ कार्यान्वित कर सकता हूं। यहाँ मैं क्या किया है:

type Expr = 
    | Con of Num 
    | Var of Name 
    | Add of Expr * Expr 
    | Sub of Expr * Expr 
    | Mult of Expr * Expr 
    | Div of Expr * Expr 
    | Pow of Expr * Expr 
    override this.ToString() = this.Stringify() 
    abstract member Stringify : unit -> string 

लेकिन मैं निम्नलिखित संकलक त्रुटि मिलती है:

error FS0912: This declaration element is not permitted in an augmentation

संदेश भी सही नहीं प्रतीत होता है (मैं अभी तक एक प्रकार वृद्धि बनाने नहीं कर रहा हूँ), लेकिन मैं समझता हूँ यह शिकायत क्यों कर रहा है। यह नहीं चाहता कि मैं एक भेदभावपूर्ण संघ के प्रकार पर एक अमूर्त सदस्य बनाना चाहता हूं क्योंकि इसे विरासत में नहीं लिया जा सकता है। भले ही मैं वास्तव में विरासत नहीं चाहता हूं, मैं चाहता हूं कि यह सी # में आंशिक वर्ग की तरह व्यवहार करे, जहां मैं इसे कहीं और परिभाषित कर सकता हूं (इस मामले में एक ही फाइल)।

मैं sprintf के साथ StructuredFormatDisplay विशेषता के देर से बाध्यकारी शक्ति का उपयोग करके "धोखा दे" समाप्त हो गया:

[<StructuredFormatDisplay("{DisplayValue}")>] 
type Expr = 
    | Con of Num 
    | Var of Name 
    | Add of Expr * Expr 
    | Sub of Expr * Expr 
    | Mult of Expr * Expr 
    | Div of Expr * Expr 
    | Pow of Expr * Expr 
    override this.ToString() = sprintf "%A" this 

/* stringify function goes here */ 

type Expr with 
    member public this.DisplayValue = stringify this 

अब हालांकि sprintf और ToString दोनों उत्पादन एक ही स्ट्रिंग, और वहाँ पाने के लिए कोई रास्ता नहीं है Add (Con 2,Con 3) आउटपुट के रूप में (2 + 3) के विपरीत अगर मैं इसे चाहता हूं।

तो क्या ऐसा करने का कोई और तरीका है जो मैं करने की कोशिश कर रहा हूं?

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

उत्तर

5

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

हालांकि, चूंकि आपने स्पष्ट होना करने के लिए अपने ड्यू प्रकार पसंद करते हैं, ड्यू एक एकल मामले में डेटा प्रकार लपेटकर पर विचार करें:

// precede this with your definitions of Expr and stringify 
type ExprWrapper = InnerExpr of Expr with 
    static member Make (x: Expr) = InnerExpr x 
    override this.ToString() = match this with | InnerExpr x -> stringify x 

// usage 
let x01 = Add(Con 5, Con 42) |> ExprWrapper.Make 
printfn "%O" x01 
// outputs: (5 + 42) 
printfn "%s" (x01.ToString()) 
// outputs: (5 + 42) 
printfn "%A" x01 
// outputs: InnerExpr(Add (Con 5,Con 42)) 

प्रशस्ति पत्र this answer से:

In complex programs clear type signatures indeed make it easier to maintain composability.

Not only it's simpler to add more cases to single-case DUs, but also it's easier to extend DUs with member and static methods.

+1

मुझे लगता है कि मैं इसे स्वीकार करूंगा, भले ही मैं इसका उपयोग नहीं कर सकता क्योंकि यह कम से कम एक तकनीक है जिस पर मैंने विचार नहीं किया था। – luksan

6

ऐसे समाधान के बारे में कैसे एक प्रकार के एक्सटेंशन की आवश्यकता नहीं है।

इसके बजाय, एक स्थिर सदस्य जो stringify है (हम जरूरत type a ... and b के रूप में डमी प्रकार b की आवश्यकता है एक प्रकार

type Num = string //missing 
type Name = string //missing 
type Expr = 
    | Con of Num 
    | Var of Name 
    | Add of Expr * Expr 
    | Sub of Expr * Expr 
    | Mult of Expr * Expr 
    | Div of Expr * Expr 
    | Pow of Expr * Expr 
    override this.ToString() = type_dummy.stringify this 
and type_dummy = 
    static member stringify expr = 
     let stringify = type_dummy.stringify 
     match expr with 
     | Con(x) -> string x 
     | Var(x) -> string x 
     | Add(x, y) -> sprintf "(%s + %s)" (stringify x) (stringify y) 
     | Sub(x, y) -> sprintf "(%s - %s)" (stringify x) (stringify y) 
     | Mult(x, y) -> sprintf "(%s * %s)" (stringify x) (stringify y) 
     | Div(x, y) -> sprintf "(%s/%s)" (stringify x) (stringify y) 
     | Pow(x, y) -> sprintf "(%s ** %s)" (stringify x) (stringify y) 
+0

हाँ, लेकिन यह है कि आवश्यकता है कि दो प्रकार स्रोत फ़ाइल में आसन्न हो, जो मैं बचने की कोशिश कर रहा था। हालांकि इसे सीधे टाइप पर रखने की आवश्यकता से बेहतर हो सकता है। – luksan

8

आप अपनी वृद्धि में ToString को परिभाषित करने पर विचार किया जा करने के लिए के साथ एक प्रकार को परिभाषित?

type Num = int 
type Name = string 

type Expr = 
    | Con of Num 
    | Var of Name 
    | Add of Expr * Expr 
    | Sub of Expr * Expr 
    | Mult of Expr * Expr 
    | Div of Expr * Expr 
    | Pow of Expr * Expr 

let rec stringify expr = 
    match expr with 
    | Con(x) -> string x 
    | Var(x) -> string x 
    | Add(x, y) -> sprintf "(%s + %s)" (stringify x) (stringify y) 
    | Sub(x, y) -> sprintf "(%s - %s)" (stringify x) (stringify y) 
    | Mult(x, y) -> sprintf "(%s * %s)" (stringify x) (stringify y) 
    | Div(x, y) -> sprintf "(%s/%s)" (stringify x) (stringify y) 
    | Pow(x, y) -> sprintf "(%s ** %s)" (stringify x) (stringify y) 

type Expr with 
    override this.ToString() = stringify this 

हालांकि, यह एक

warning FS0060: Override implementations in augmentations are now deprecated. Override implementations should be given as part of the initial declaration of a type. 
+0

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

+0

दिलचस्प बात यह है कि यह केवल तभी काम करता है जब मैं पूरी स्क्रिप्ट को एक बार में निष्पादित करता हूं (इसे वीएस में हाइलाइट करके और ALT-ENTER दबाकर)। यदि मैं अलग-अलग प्रकार की परिभाषा और संवर्धन को निष्पादित करने का प्रयास करता हूं, तो मुझे 'त्रुटि FS0854 मिलती है: विधि ओवरराइड और इंटरफ़ेस कार्यान्वयन की अनुमति नहीं है' क्योंकि F # कंपाइलर पहले से मौजूद प्रकार पर किसी विधि को ओवरराइड नहीं कर सकता है। हो सकता है कि दोहरी व्यवहार यही कारण है कि उन्होंने व्यवहार को बहिष्कृत किया। – luksan

+3

ठीक है, एक प्रकार * वृद्धि * संकलित होने पर, उसी कक्षा में समाप्त होता है। जबकि एक प्रकार * एक्सटेंशन * एक विस्तार विधि के रूप में समाप्त होता है। वाक्यविन्यास वही है, हालांकि, मैं समझ सकता हूं कि आपको इसे एक ही समय में निष्पादित करना होगा। (जरूरी नहीं कह रहा है, यह बहुत सहज है) –

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