2011-11-15 58 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