Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I write an enumeration in F# without explicitly assigning number literals?

Tags:

enums

f#

I have an enumeration in F#, like this:

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

I do not like manually typing out numbers, and I want to easily insert more items in the enumeration later without having to shift the numbers.

I tried this

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

but it caused an 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 assigning number values to each item?

like image 292
Peter Olson Avatar asked Aug 16 '11 19:08

Peter Olson


People also ask

How do you declare an enumeration?

The keyword 'enum' is used to declare new enumeration types in C and C++. Following is an example of enum declaration. // The name of enumeration is "flag" and the constant // are the values of the flag.

What is the format of enumeration?

In computer programming, an enumerated type (also called enumeration, enum, or factor in the R programming language, and a categorical variable in statistics) is a data type consisting of a set of named values called elements, members, enumeral, or enumerators of the type.

What is an enumeration example?

Enumeration means counting or reciting numbers or a numbered list. A waiter's lengthy enumeration of all the available salad dressings might seem a little hostile if he begins with a deep sigh. When you're reciting a list of things, it's enumeration.

What are enumeration data types give example?

An enumerated type is a type whose legal values consist of a fixed set of constants. Common examples include compass directions, which take the values North, South, East and West and days of the week, which take the values Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, and Saturday.


2 Answers

Unfortunately, you can't. Are the numeric values important? If so, it somewhat escapes the intended use of enums (flags aside). You might consider a class or discriminated union in that case.

Your second example is, in fact, a discriminated union. But your later use of EnumOfValue, which expects an enum, is causing the error.

Another option is to store the enum to number mapping in a dictionary and replace the pattern matching with a dictionary lookup. Then, the numeric value of the enum would be irrelevant.

I agree that manually managing enum values is burdensome. I hope it's addressed in a future version.

like image 88
Daniel Avatar answered Oct 04 '22 19:10

Daniel


As Daniel says, you can't define an enum without specifying the numeric equivalents. However, you can define a function which converts a number to the corresponding case of a discriminated 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'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 generic function like this:

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 that the pattern match is now total.

like image 34
kvb Avatar answered Oct 04 '22 19:10

kvb