开发者

Using all cases of union type F#

This is very linked to the question here How to enumerate an enum/type in F#. I define a union type and then I need to use all the possible cases of the type in static method. For example:

type Interests =
| Music 
| Books
| Movies
    with 
        static member GetValue( this) = match this with 
                                         | Music  -> 0
                                         | Books -> 5
                                         | Movies -> 0
        static member GetSeqValues() = allCases|>Seq.map(GetValue)

How do I get allCas开发者_如何转开发es ?

Thanks a lot


You can use FSharpType.GetUnionCases() from Microsoft.FSharp.Reflection to get all cases of a discriminated union. In your example, it would look like this:

type Interests = 
   | Music  
   | Books 
   | Movies
   static member GetValue(this) = (...)
   static member GetSeqValues() = 
     // Get all cases of the union
     let cases = FSharpType.GetUnionCases(typeof<Interests>)
     [ for c in cases do 
         // Create value for each case (assuming it has no arguments)
         let interest = FSharpValue.MakeUnion(c, [| |]) :?> Interests
         yield GetValue(interest) ]

However, the problem is that you may not be able to create instances to pass to your GetValue member, because some cases may have arguments (when calling MakeUnion you have to pass it an array of arguments and I used just an empty array). For example if you had:

type Sample =
 | A of int
 | B of bool


I made an expansion of Tomas' work that handles making any type of FSharp Union (handles properties of the union case) as long as you provide it the logic for what to do with non-union children.

let rec getAllDUCases fNonUnionArg t : obj list =
    let getAllDUCases = getAllDUCases fNonUnionArg
    // taken from http://stackoverflow.com/questions/6497058/lazy-cartesian-product-of-multiple-sequences-sequence-of-sequences
    let cartesian_product2 sequences = 
        let step acc sequence = seq {
            for x in acc do
            for y in sequence do
            yield seq { yield! x; yield y}}
        Seq.fold step (Seq.singleton Seq.empty) sequences

    let makeCaseTypes (fUnion:Type-> obj list) (fNonUnionArg:Type -> obj) (uc: UnionCaseInfo) : UnionCaseInfo*(obj list list) =
        let constructorArgs = 
            uc.GetFields() 
            |> Seq.map (fun f -> 
                if FSharpType.IsUnion f.PropertyType then 
                    let childTypes = fUnion f.PropertyType 
                    if 
                        childTypes
                        |> Seq.exists (fun ct -> FSharpType.IsUnion (ct.GetType()) |> not) then
                            failwithf "fUnion returned a bad type in list %A" childTypes
                    childTypes
                else [ fNonUnionArg f.PropertyType] )
            |> List.ofSeq
        let allCombinationsOfFieldPossibles = 
            cartesian_product2 constructorArgs
            |> Seq.map List.ofSeq
            |> List.ofSeq
        uc, allCombinationsOfFieldPossibles
    // with help from http://stackoverflow.com/a/4470670/57883
    let result =
        FSharpType.GetUnionCases t
        |> Seq.map (makeCaseTypes getAllDUCases fNonUnionArg)
        |> List.ofSeq
    let result = 
        result
        |> Seq.map (fun (uc,allFieldComboCases) -> allFieldComboCases |> Seq.map (fun args-> FSharpValue.MakeUnion(uc,args |> Array.ofList)))
        |> Seq.collect id
        |> Seq.map box
        |> List.ofSeq
    result
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜