Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to iterate over an Enum, and cast the obj

How to enumerate an enum/type in F# tells us how to get an enumerator for a .Net enum type in F#:

Use: Enum.GetValues(typeof<MyType>)

However, when I put that into use I found a limitation. I can work around that limitation, but I am looking for a better way.

The problem is that the solution returns a .Net Array of Objects, but to use it we need to cast it, and that casting is unwieldy for iteration.

type Colors =
| Red = 0
| Blue = 1
| Green = 2

// Strongly typed function, to show the 'obj' in real use
let isFavorite color = color = Colors.Green

// Iterate over the Enum (Colors)
for color in Enum.GetValues(typeof<Colors>) do
    printfn "Color %A. Is Favorite -> %b" color (isFavorite color)  // <-- Need to cast

The (IsFavorite color) raises a type conflict between Colors (expected) and obj (actual)

This is easily fixed:

for obj in Enum.GetValues(typeof<Colors>) do
    printfn "Color %A. Is Favorite -> %b" obj (isFavorite (unbox<Colors> obj))

But, what if one needs that (unbox<Colors> obj) in several places?

A local let color = ... will suffice, but, ideally, we would use an enumerable-expression which returns a seq<Colors>.

I have been able to build that expression, but it is: a. difficult to build, and b. long winded.

let colorsSeq = 
     Seq.cast (Enum.GetValues(typeof<Colors>)) 
     |> Seq.map (fun o -> unbox<Colors> o)

for color in colorsSeq do
    printfn "Color %A. Is Favorite -> %b" color (isFavorite color)

Is there a better expression?

like image 732
Stephen Hosking Avatar asked Dec 25 '22 08:12

Stephen Hosking


1 Answers

Enum.GetValues is an old BCL API, so there's nothing much you can do about it... I don't think you can get what you want in a significantly more concise way than what you already have, but you can reduce it a bit:

let colorsSeq = Enum.GetValues(typeof<Colors>) |> Seq.cast<Colors>

If you need to do something like this a lot, you could consider packaging it in a generic function:

module Enum = 
    let values<'a> = Enum.GetValues(typeof<'a>) |> Seq.cast<'a>

Which would enable you to use it like this:

for color in Enum.values<Colors> do
    printfn "Color %A. Is Favorite -> %b" color (isFavorite color)
like image 54
Mark Seemann Avatar answered Jan 01 '23 23:01

Mark Seemann