F#:在DU情况下进行类型匹配,使其稍微更通用

时间:2015-02-27 20:16:25

标签: f# expression discriminated-union

this previous question中,有一个可爱的解决方案来询问对象是否是特定的联合案例:

let isUnionCase (c : Expr<_ -> 'T>)  = 
    match c with
    | Lambdas (_, NewUnionCase(uci, _)) ->
        let tagReader = Microsoft.FSharp.Reflection.FSharpValue.PreComputeUnionTagReader(uci.DeclaringType)
        fun (v : 'T) -> (tagReader v) = uci.Tag
    | _ -> failwith "Invalid expression"

这很棒。如果我有:

type Dog = 
    | Spaniel
    | Shepherd
type Cat =
    | Tabby
    | Manx
type Animal
    | Dog of Dog
    | Cat of Cat

我可以通过Animal询问是否有任何特定的isUnionCase <@ Animal.Dog @> someAnimal是特定的动物。

我想做的是:

let typesMatch (c:Animal) t = isUnionCase t c

let rec typematch animals types =
match (animals, types) with
| ([], []) -> true
| (animal::atail, ty::tytail) -> if typesMatch animal ty then typematch atail tytail else false
| (_, _) -> false

typematch [ Animal.Dog(Spaniel); Animal.Cat(Tabby) ] [ <@ Animal.Dog @> ; <@ Animal.Cat @>]

上生成编译器错误

原因是第二个列表无效,因为它不是同质的,即使它们都是动物案例。

如何充分地对此进行整合,以便您可以询问谓词“这个对象列表是否与受歧视的联合的所有情况都匹配描述其预期案例类型的表达式列表?”

1 个答案:

答案 0 :(得分:2)

使用无类型引文<@@ ... @@>代替类型引用,并使用可以处理这些内容的isUnionCase形式:

open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Reflection

let rec isUnionCase = function
| Lambda (_, expr) | Let (_, _, expr) -> isUnionCase expr
| NewTuple exprs -> 
    let iucs = List.map isUnionCase exprs
    fun value -> List.exists ((|>) value) iucs
| NewUnionCase (uci, _) ->
    let utr = FSharpValue.PreComputeUnionTagReader uci.DeclaringType
    box >> utr >> (=) uci.Tag
| _ -> failwith "Expression is no union case."

type Dog = 
    | Spaniel
    | Shepherd
type Cat =
    | Tabby
    | Manx
type Animal =
    | Dog of Dog
    | Cat of Cat

let typesMatch (c:Animal) t = isUnionCase t c

let rec typematch animals types =
    match (animals, types) with
    | ([], []) -> true
    | (animal::atail, ty::tytail) -> if typesMatch animal ty then typematch atail tytail else false
    | (_, _) -> false

typematch [ Animal.Dog(Spaniel); Animal.Cat(Tabby) ] [ <@@ Animal.Dog @@> ; <@@ Animal.Cat @@>]
|> printfn "Result: %b"

System.Console.ReadKey true |> ignore

此外,我使用了isUnionCase的pimped up版本,如here所述,它可以处理如下表达式:

isUnionCase <@ Spanial, Shepherd @>

...匹配任何西班牙人或牧羊人。