F#:DU情况下的类型匹配,使其更通用 [英] F#: type matching on DU cases, make this slightly more generic
问题描述
在上一个问题中,有一个一个很好的解决方案,询问一个对象是否是一个特定的联合案例:
In this previous question, there is a lovely solution to asking if an object is a particular union case:
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
是特定的动物.
I can ask if any particular Animal
is a specific animal by doing isUnionCase <@ Animal.Dog @> someAnimal
.
我想做的事情是这样的:
What I'd like to do is something this:
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 @>]
原因是第二个列表无效,因为它们都不是同质的,即使它们都是动物案例.
The reason being that the second list invalid since it is not homogeneous, even though they are both Animal cases.
如何充分概括这一点,以使您可以问谓词:这是一个已区分联合的所有案例的对象列表是否与描述其预期案例类型的表达式列表相匹配?"
How does one generify this sufficiently so that you can ask the predicate "does this list of objects which are all cases of a discriminated union match the list of expressions describing their expected case types?"
推荐答案
使用无类型的引号<@@ ... @@>
而不是类型的引号,并使用isUnionCase
的形式可以处理这些引号:
Use untyped quotations <@@ ... @@>
instead of typed quotations, and use a form of isUnionCase
that can deal with those:
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
的简化版本,该版本可以处理以下表达式:
Additionally, I used my pimped up version of isUnionCase
as described here, which can deal with expressions like:
isUnionCase <@ Spanial, Shepherd @>
...与任何Spanial或Shepherd匹配.
...which matches anything that is a Spanial or Shepherd.
这篇关于F#:DU情况下的类型匹配,使其更通用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!