2011-11-15 51 views
2

比方說,我有一個顯著類層次結構:F#:將歧視聯合體和類層次結合在一起?

Tag 
    ControlFlowTag 
     IfTag 
     ForTag 
    JumpTag 
    HTMLTag 
     DivTag 

,我想和這些和字符串穿插列表。

let MyList = [tagA, tagB, "some text", tagC] 

,我想我能區分聯合它

type Node = 
    | Tag of Tag 
    | String of String 

let MyList: list<Node> = [tagA, tagB, "some text", tagC] 

但很可惜,它不無

let MyList: list<Node> = [Tag tagA, Tag tagB, String "some text", Tag tagC] 

顯然,標籤工作,並在字符串描述節點是正交獨立來自現有的Tag/String類。鼠標懸停給我的類型爲Node.TagNode.String,這不是我想要的。

我現在已經是一個功能t它創建了一個StringTagTag繼承,給我

let MyList : list<Tag> = [tagA, tagB, t"some text", tagC] 

這是相當不錯的,但額外的t增加了視覺噪聲。我真正想要的是一個強類型的「兩種不同類型的清單」,我可以使用match聲明來處理這些清單。我認爲這是歧視聯盟的重點,但是他們無法使用現有的類型層次結構是一個問題,因爲現有的層次結構(在本例中爲Tag)足夠複雜,我認爲對該類型子集的完整的OO繼承方法更清晰而不是純粹的歧視聯盟方法

其中一種方法是將其設置爲obj的列表,並在match之前/期間投射所有內容,但這並不是很好。還有其他方法嗎?

回答

8

如果你有兩個不同的下游用戶,說

type Node = 
    | Tag of Tag 
    | String of String 

type Foo = 
    | Bar of Tag 
    | Name of String 

如何將編譯器知道哪些類型的下面的列表是什麼?

[tagA; tagB; "some text"; tagC] 

正如svick所說,鑑別器是必要的。如果您使用類,則需要將其轉換爲基本類型,所以我不確定是否節省了擊鍵。

如果你正在使用字典,here是一個很好的選擇,以減少拳擊的句法噪音。也許你可以爲列表做類似的事情。

+0

甚至:'type Node = Bar of Bar |標記Foo' –

2

歧視的工會就是這樣 - 歧視(不像C工會)。這意味着您必須始終添加鑑別器。

如果這是C#,我會考慮從stringStringTag的隱式轉換。但是由於F#不支持隱式轉換,我認爲第二種方法是最好的選擇。雖然我會讓這個函數的名字更具描述性,但不僅僅是t。大多數時候,最好編寫易於閱讀的代碼,而不是易於編寫的代碼。

8

我不知道這是多麼有幫助,但您可以使用活動模式以類似DU的方式匹配類層次結構(如果適用)。

[<AbstractClass>] 
type Animal() = 
    abstract Talk : string 

type Cat() = 
    inherit Animal() 
    override this.Talk = "Meow" 

type Dog() = 
    inherit Animal() 
    override this.Talk = "Woof" 

type SuperCat(s) = 
    inherit Cat() 
    override this.Talk = s 

let animals : list<Animal> = 
    [Dog(); Cat(); SuperCat("MEOW")] 

let (|SCSaid|_|) (a:Animal) = // Active Pattern 
    match a with 
    | :? SuperCat as sc -> Some sc.Talk 
    | _ -> None 

for a in animals do 
    match a with 
    | :? Dog -> printfn "dog"  
    | SCSaid s -> printfn "SuperCat said %s" s // looks like DU 
    | _ -> printfn "other" 
//dog 
//other 
//SuperCat said MEOW