F #: объединение разрозненных союзов и иерархий классов? - PullRequest
2 голосов
/ 15 ноября 2011

Допустим, у меня есть значительная иерархия классов:

Tag
    ControlFlowTag
        IfTag
        ForTag
    JumpTag
    HTMLTag
        DivTag

, и я хочу составить список из этих строк и строк.это

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 и Node.String, а это не то, что я хочу.

Теперь у меня есть функция t, которая создает StringTag, которая наследуется от Tag, давая мне

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

, что довольно приятно, но дополнительные t добавляют визуальный шум.На самом деле мне нужен строго типизированный «список двух разных типов», с которым я мог бы работать, используя операторы match.Я думал, что в этом и заключается смысл дискриминированных союзов, но их неспособность использовать существующие иерархии типов является проблемой, поскольку существующая иерархия (в данном случае Tag) достаточно сложна, я думаю, что подход полного OO-наследования для этого подмножества типовэто яснее, чем чистый подход Дискриминационного союза

Один из вариантов - просто составить список из obj и разыграть все до / во время match, но это не очень хорошо.Есть ли другие подходы?

Ответы [ 3 ]

8 голосов
/ 15 ноября 2011

Если бы у вас было два разных DU, скажем

type Node = 
  | Tag of Tag
  | String of String

и

type Foo = 
  | Bar of Tag
  | Name of String

как бы компилятор узнал, какого типа следующий список?

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

Как сказал Свик, дискриминатор необходим.Если вместо этого вы используете классы, вам нужно перейти на базовый тип, поэтому я не уверен, что вы сэкономите на нажатиях клавиш.

Если вы работаете со словарями, здесь - этоХороший вариант для уменьшения синтаксического шума бокса.Может быть, вы можете сделать что-то подобное для списков.

8 голосов
/ 15 ноября 2011

Я не знаю, насколько это полезно, но вы можете использовать активные шаблоны для соответствия иерархии классов 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
2 голосов
/ 15 ноября 2011

Дискриминационные союзы - это просто дискриминация (в отличие, например, от профсоюзов C)Это означает, что вы должны всегда добавлять дискриминатор.

Если бы это был C #, я бы подумал о неявном преобразовании из string в StringTag.Но поскольку F # не поддерживает неявные преобразования, я думаю, что второй подход - ваша лучшая ставка.Хотя я бы сделал имя функции более наглядным, а не просто t.В большинстве случаев лучше писать код, который легко читать, а не код, который легко писать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...