How to determine if a list of discriminatory union types has the same case?

Suppose I have a DU, for example:

type DU = Number of int | Word of string 

And let me create a list of them:

 [Number(1); Word("abc"); Number(2)] 

How can I write a function that will return true for a DU list, where all the elements are the same case. For the above list, it should return false.

+5
source share
2 answers

The general approach that I would use here would be to match the join values ​​in the tags identifying the cases, and then check if the resulting tag set has at most one element.

 let allTheSameCase (tagger: 'a -> int) (coll: #seq<'a>) = let cases = coll |> Seq.map tagger |> Set.ofSeq Set.count cases <= 1 

For the tagger function, you can assign tags manually:

  allTheSameCase (function Number _ -> 0 | Word _ -> 1) lst 

or use reflection (note that you may need to set the anchor flags as needed):

  open Microsoft.FSharp.Reflection let reflectionTagger (case: obj) = let typ = case.GetType() if FSharpType.IsUnion(typ) then let info, _ = FSharpValue.GetUnionFields(case, typ) info.Tag else -1 // or fail, depending what makes sense in the context. 
+5
source

In case you want to check that list items have a specific case of union, just provide a predicate function.

 let isNumbers = List.forall (function Number _ -> true | _ -> false) 

If you don’t care what kind of union case, if they are all the same, you need to clearly indicate them all. Prohibiting reflection magic in order to get a property that is not exposed inside F #, you also need to assign some value to each case. To avoid the need to come up with arbitrary values, we can use an active template that appears in different DUs behind the scenes.

 let (|IsNumber|IsWord|) = function | Number _ -> IsNumber | Word _ -> IsWord let isSameCase src = src |> Seq.groupBy (|IsNumber|IsWord|) |> Seq.length <= 1 
+4
source

All Articles