在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 @>]
原因是第二个列表无效,因为它不是同质的,即使它们都是动物案例。
如何充分地对此进行整合,以便您可以询问谓词“这个对象列表是否与受歧视的联合的所有情况都匹配描述其预期案例类型的表达式列表?”
答案 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 @>
...匹配任何西班牙人或牧羊人。