9

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 allCases ?

Thanks a lot

Community
  • 1
  • 1
jlezard
  • 1,417
  • 2
  • 15
  • 32

2 Answers2

17

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
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • so Microsoft.FSharp.Reflection.FSharpType.GetUnionCases(typeof) returns a seq of UnionCaseInfo, from which I cant seem to get a seq of Interests – jlezard Dec 17 '10 at 13:09
  • @jlezard: I added more information (I had to run away for a few minutes.. :-)) – Tomas Petricek Dec 17 '10 at 13:19
0

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
Maslow
  • 18,464
  • 20
  • 106
  • 193