【问题标题】:F#: combining together discriminated unions and class hierarchies?F#:将有区别的联合和类层次结构结合在一起?
【发布时间】:2011-12-29 03:33:36
【问题描述】:

假设我有一个重要的类层次结构:

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]

显然,Node 中描述的 Tag 和 String 是正交的,并且与现有的 Tag/String 类分开。将鼠标悬停会给我Node.TagNode.String 这样的类型,这不是我想要的。

我现在拥有的是一个函数t,它创建了一个继承自TagStringTag,给了我

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

这很不错,但是额外的t 增加了视觉噪音。我真正想要的是一个强类型的“两种不同类型的列表”,我可以使用match 语句来处理它。我认为这是区分联合的重点,但它们无法使用现有的类型层次结构是一个问题,因为现有的层次结构(在本例中为 Tag)足够复杂,我认为该类型子集的完整 OO 继承方法比纯粹的歧视联盟方法更清晰

一种选择是将其设为obj 的列表并在match 之前/期间强制转换所有内容,但这并不是很好。还有其他方法吗?

【问题讨论】:

    标签: f# discriminated-union


    【解决方案1】:

    有区别的联合就是这样 - 有区别的(与例如 C 联合不同)。这意味着您必须始终添加鉴别器。

    如果这是 C#,我会考虑从 stringStringTag 的隐式转换。但由于 F# 不支持隐式转换,我认为第二种方法是您最好的选择。虽然我会让函数的名称更具描述性,而不仅仅是t。大多数时候,最好编写易于阅读的代码,而不是易于编写的代码。

    【讨论】:

      【解决方案2】:

      如果你有两个不同的 DU,比如说

      type Node = 
        | Tag of Tag
        | String of String
      

      type Foo = 
        | Bar of Tag
        | Name of String
      

      编译器如何知道以下列表是哪种类型?

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

      正如 svick 所说,鉴别器是必要的。如果您改用类,则需要向上转换为基本类型,因此我不确定您是否可以节省击键。

      如果您使用字典,here 是减少装箱语法噪音的好选择。也许你可以对列表做类似的事情。

      【讨论】:

      • 甚至:type Node = Bar of Tag | Foo of Tag
      【解决方案3】:

      我不知道这有多大帮助,但如果合适的话,您可以使用 Active Patterns 以类似于 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
      

      【讨论】:

        猜你喜欢
        • 2011-11-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-19
        • 2018-11-24
        • 2014-03-01
        相关资源
        最近更新 更多