F#:DU情况下的类型匹配,使其更通用 [英] F#: type matching on DU cases, make this slightly more generic

查看:112
本文介绍了F#:DU情况下的类型匹配,使其更通用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

上一个问题中,有一个一个很好的解决方案,询问一个对象是否是一个特定的联合案例:

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆