2011-11-15 10 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] 

タグなしでは動作しません。また、文字列がノードに記述直交し、分離されている労働組合を区別できると思っていました既存のタグ/文字列クラスから削除します。マウスオーバーで私にはNode.TagNode.Stringというタイプが与えられますが、これは私が望むものではありません。

私が今持っていることは私にはかなりいいですが、余分なtが視覚的ノイズに追加されます

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

を与え、Tagから継承StringTagを作成する関数tです。私が実際に望んでいるのは、matchステートメントを使用して作業できる強く型付けされた "2つの異なるタイプのリスト"です。私はこれがDiscriminated Unionsのポイントだと思っていましたが、既存の階層(この場合はTag)が複雑なので、既存の型階層を使用できないという問題があります。そのタイプのサブセットに対する完全なOO継承アプローチがより明確である純粋に差別化された同盟のアプローチより

1つの選択肢はobjのリストにしてmatchの前後にすべてをキャストすることですが、これはあまりうまくありません。他の方法はありますか?

答えて

8

使用すると、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は、ボクシングの構文的なノイズを減らすのに最適なオプションです。たぶんあなたはリストのために似たようなことをすることができます。

+0

または: 'type Node = Bar of Tag |タグのフー ' –

2

差別化されたユニオンは、それだけで差別化されています(たとえば、Cのユニオンとは異なります)。つまり、常にディスクリミネータを追加する必要があります。

これがC#の場合は、stringからStringTagへの暗黙的な変換が考えられます。しかし、F#は暗黙的な変換をサポートしていないので、2番目のアプローチが最善の策だと思います。私は関数の名前をよりわかりやすくするつもりだが、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 
関連する問題