17

I've got an enumeration type defined like so:

type tags = 
    | ART  = 0
    | N    = 1
    | V    = 2 
    | P    = 3
    | NULL = 4

is there a way to do a for ... in tags do ?

This is the error that I'm getting:

The value, constructor, namespace or type tags is not defined

jason
  • 236,483
  • 35
  • 423
  • 525
Jared
  • 5,977
  • 10
  • 38
  • 48

8 Answers8

13

How about:

let enumToList<'a> = (Enum.GetValues(typeof<'a>) :?> ('a [])) |> Array.toList

This has the advantage of providing a strongly typed list

To use just do:

let tagList = enumToList<tags>
bill
  • 131
  • 1
  • 2
10

Use Enum.GetValues:

let allTags = Enum.GetValues(typeof<tags>)
jason
  • 236,483
  • 35
  • 423
  • 525
  • It should be Enum.GetValues(typeof). I had tried that earlier but didn't know of the different syntax for typeof. Thanks! – Jared Feb 02 '10 at 04:58
  • the code you first provided **did not** work against the code Jared posted. If another reader happened to bump into this question, he would have thought that Enum.GetValues could work with unions, which is not the case. Now that you explained I reverted my -1 – Mauricio Scheffer Feb 02 '10 at 14:33
  • As a heads up, Enum.GetValues does not exist in the .NetCF or in the silverlight runtime. – rysama Feb 02 '10 at 18:55
  • 2
    My problem with this is that GetValues will return an Array and I can't get it to cast back to an enum. I've tried `enum tag` but that didn't work. – Jared Feb 03 '10 at 15:18
  • @Jared, see my answer for dealing with the Array returned by GetValues – Kristopher Johnson May 01 '14 at 00:13
6

Here is a complete example that prints information about any discriminated union. It shows how to get cases of the discriminated union and also how to get the fields (in case you needed them). The function prints type declaration of the given discriminated union:

open System
open Microsoft.FSharp.Reflection

let printUnionInfo (typ:Type) = 
  printfn "type %s =" typ.Name
  // For all discriminated union cases
  for case in FSharpType.GetUnionCases(typ) do
    printf "  | %s" case.Name
    let flds = case.GetFields()
    // If there are any fields, print field infos
    if flds.Length > 0 then 
      // Concatenate names of types of the fields
      let args = String.concat " * " [ for fld in flds -> fld.PropertyType.Name ] 
      printf " of %s" args
    printfn ""    

// Example
printUnionInfo(typeof<option<int>>)
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
  • 2
    The original type declaration in the post (before your edit) was a discriminated union though... Enums are of course useful in F# too, but in only a few situations. – Tomas Petricek Feb 03 '10 at 15:05
  • The only edit I made to the post was to fix a typo from "eumeration" to "enumeration." I think based on the subject that the OP always intended for the question to be about enums but introduced confusion in not correctly defining `tags` as an enum. – jason Feb 03 '10 at 17:10
  • 1
    The original question was showing a discriminated union--not an enum. @Chris Smith added the " = " to the original question after the fact and completely changed the type the original poster asked about. I'm not sure that was such a wise edit because it completely changes the nature of the question. – Onorio Catenacci Feb 03 '10 at 17:16
  • @Onorio Catenacci: I agree. There is definitely a lot of confusion as to what the OP meant. He stated "enumeration" in the subject, but defined a discriminated union in his post. – jason Feb 03 '10 at 17:22
  • @Tomas Petricek: No problem Tomas; there's just a lot of confusion here! By the way, I love your book Real-World Functional Programming. – jason Feb 03 '10 at 17:36
3

To make it an enum you need to explicitly give values to each case, otherwise it's a union type:

type tags = 
    | ART = 0
    | N = 1
    | V = 2
    | P = 3
    | NULL= 4
let allTags = System.Enum.GetValues(typeof<tags>)
Robert
  • 6,407
  • 2
  • 34
  • 41
3

Robert's right about how to generate an actual enum and get its cases. If you have a true union type, you can get the cases via the Microsoft.FSharp.Reflection.FSharpType.GetUnionCases function.

kvb
  • 54,864
  • 2
  • 91
  • 133
3

You can use Enum.GetValues, which returns an Array of objects that you then have to downcast to integer values. (Note: I'm using Mono's F# implementation; maybe things are different with .NET.)

Here are some functions I wrote to get a list of all enumeration values and to get the min and max values:

open System

module EnumUtil =

    /// Return all values for an enumeration type
    let EnumValues (enumType : Type) : int list =
        let values = Enum.GetValues enumType
        let lb = values.GetLowerBound 0
        let ub = values.GetUpperBound 0
        [lb .. ub] |> List.map (fun i -> values.GetValue i :?> int) 

    /// Return minimum and maximum values for an enumeration type
    let EnumValueRange (enumType : Type) : int * int =
        let values = EnumValues enumType
        (List.min values), (List.max values)
Kristopher Johnson
  • 81,409
  • 55
  • 245
  • 302
2

In .Net 5 there is a generic overload of Enum.GetValues which eliminates the need for casting.

Enum.GetValues<T>()
0
type Options = 
    | Exit          = 0
    | CreateAccount = 1

Console.WriteLine()
Console.WriteLine("Choose an option:")
let allOptions = Enum.GetValues(typeof<Options>)
for option in allOptions do
    if (option <> null) then
        Console.WriteLine(sprintf "%d: %s" (option :?> int) (option.ToString()))
let optionChosen = System.Console.ReadLine()
ympostor
  • 909
  • 7
  • 16