How to write an enumeration in F # without explicitly assigning numeric literals?

I have an enumeration in F #, for example:

type Creature = | SmallCreature = 0 | MediumCreature = 1 | GiantCreature = 2 | HumongousCreature = 3 | CreatureOfNondescriptSize = 4 

I don’t like manually typing numbers, and I want to easily insert more elements into the enumeration later, without rearranging the numbers.

I tried this

 type Creature = | SmallCreature | MediumCreature | GiantCreature | HumongousCreature | CreatureOfNondescriptSize 

but this caused the error The type 'Creature' is not a CLI enum type later in the program

 let input = Int32.Parse(Console.ReadLine()) let output = match EnumOfValue<int, Creature>(input) with // <---Error occurs here | Creature.SmallCreature -> "Rat" | Creature.MediumCreature -> "Dog" | Creature.GiantCreature -> "Elephant" | Creature.HumongousCreature -> "Whale" | Creature.CreatureOfNondescriptSize -> "Jon Skeet" | _ -> "Unacceptably Hideous Monstrosity" Console.WriteLine(output) Console.WriteLine() Console.WriteLine("Press any key to exit...") Console.Read() |> ignore 

How can I define an enumeration without manually linking the values ​​of the numbers to each element?

+4
source share
2 answers

Sorry, you can’t. Are numerical values ​​important? If so, this somewhat eludes the intended use of enumerations (flags to the side). In this case, you may consider a class or a discriminated union.

The second example is, in fact, a discriminatory union. But your subsequent use of EnumOfValue , which is awaiting enumeration, raises an error.

Another option is to save the enumeration to numerical matching in the dictionary and replace the pattern matching with dictionary search. Then the numerical value of the enumeration will be irrelevant.

I agree that manually managing enumeration values ​​is cumbersome. I hope this will be discussed in a future version.

+5
source

As Daniel says, you cannot define an enumeration without specifying numerical equivalents. However, you can define a function that converts a number to the corresponding case of a delimited union:

 open Microsoft.FSharp.Reflection let intToDU<'t> n = if not (FSharpType.IsUnion typeof<'t>) then failwithf "%s is not a discriminated union" typeof<'t>.Name let cases = FSharpType.GetUnionCases(typeof<'t>) if n >= cases.Length || n < 0 then failwithf "%i is out of the range of %s cases (0 - %i)" n typeof<'t>.Name (cases.Length - 1) let uc = cases.[n] if uc.GetFields().Length > 0 then failwithf "%s.%s requires constructor arguments" typeof<'t>.Name uc.Name FSharpValue.MakeUnion(uc, [||]) :?> 't 

Then you can use this general function as follows:

 type Creature = | SmallCreature | MediumCreature | GiantCreature | HumongousCreature | CreatureOfNondescriptSize let input = int (System.Console.ReadLine()) let output = match intToDU input with | SmallCreature -> "Rat" | Creature.MediumCreature -> "Dog" | Creature.GiantCreature -> "Elephant" | Creature.HumongousCreature -> "Whale" | Creature.CreatureOfNondescriptSize -> "Jon Skeet" 

This has the added benefit of matching patterns.

+4
source

All Articles