F#:在 DU 案例上进行类型匹配,使其更通用
F#: type matching on DU cases, make this slightly more generic
在 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
我可以通过 isUnionCase <@ Animal.Dog @> someAnimal
.
询问任何特定的 Animal
是否是特定的动物
我想做的是:
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 @>]
上生成编译器错误
原因是第二个列表无效,因为它不是同质的,即使它们都是动物案例。
如何充分泛化这一点,以便您可以询问谓词 "does this list of objects which are all cases of a discriminated union match the list of expressions describing their expected case types?"
使用无类型引号 <@@ ... @@>
而不是类型化引号,并使用可以处理这些的 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
的改进版本,如 here 所述,它可以处理如下表达式:
isUnionCase <@ Spanial, Shepherd @>
...匹配任何西班牙犬或牧羊犬。
在 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
我可以通过 isUnionCase <@ Animal.Dog @> someAnimal
.
Animal
是否是特定的动物
我想做的是:
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 @>]
原因是第二个列表无效,因为它不是同质的,即使它们都是动物案例。
如何充分泛化这一点,以便您可以询问谓词 "does this list of objects which are all cases of a discriminated union match the list of expressions describing their expected case types?"
使用无类型引号 <@@ ... @@>
而不是类型化引号,并使用可以处理这些的 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
的改进版本,如 here 所述,它可以处理如下表达式:
isUnionCase <@ Spanial, Shepherd @>
...匹配任何西班牙犬或牧羊犬。