Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define an enum constant using the underlying value

What is the F# equivalent of this C#:

const MyEnum None = (MyEnum)1;

This does not work:

[<Literal>]
let None : MyEnum = enum 1 //ERROR: not a valid constant expression

although, curiously, it's okay in an attribute constructor:

[<MyAttribute(enum 1)>]
type T = class end

The discrepancy seems odd.

UPDATE

This is fixed in v3.1 and works as expected.

like image 361
Daniel Avatar asked Jan 25 '13 22:01

Daniel


People also ask

How do you assign a value to the constant of an enum?

To hold the value of each constant you need to have an instance variable (generally, private). You cannot create an object of an enum explicitly so, you need to add a parameterized constructor to initialize the value(s). The initialization should be done only once.

What is enum it is used to define constants?

An enumeration is a data type that consists of a set of named values that represent integral constants, known as enumeration constants. An enumeration is also referred to as an enumerated type because you must list (enumerate) each of the values in creating a name for each of them.

How do you find the enum constant?

You can use the name() method to get the name of any Enum constants. The string literal used to write enum constants is their name. Similarly, the values() method can be used to get an array of all Enum constants from an Enum type.

What is underlying type of enum?

Each enum type has a corresponding integral type called the underlying type of the enum type. This underlying type shall be able to represent all the enumerator values defined in the enumeration. If the enum_base is present, it explicitly declares the underlying type.


2 Answers

I believe the observed compiler behavior is intentional and completely in line with restrictions for values having [<Literal>] attribute defined by F# Language Spec $10.2.2:

The right-hand side expression must be a literal constant expression that is made up of either:

  • A simple constant expression, with the exception of (), native integer literals, unsigned native integer literals, byte array literals, BigInteger literals , and user-defined numeric literals. —OR—
  • A reference to another literal

Consider

type MyEnum =
| Case1 = 1
| Case2 = 2

then

[<Literal>]
let Valid: MyEnum = MyEnum.Case1 // Literal enumeration case on the right

will happily compile, but

[<Literal>]
let Invalid: MyEnum = enum<MyEnum>(1) // Expression on the right
                                      // generating constant value, which
                                      // potentially might be completely off
                                      // legit MyEnum cases

will not, although outside of [<Literal>] context both statements will compile into absolutely identical IL.

Assuming that [<Literal>] attribute is the only F# way of making C# const equivalent the only option for defining enumeration literal value would be using a literal enumeration case of proper type on the right side of let.

like image 81
Gene Belitski Avatar answered Sep 30 '22 06:09

Gene Belitski


The discrepancy is caused by the fact that C# (MyEnum)0 is indeed a literal, but F# enum is a function of type int32 -> 'T.
I believe it would not be difficult for F# team to add special processing for this construct, but unfortunately it is not there yet.

Nevertheless, there is one way to accomplish what you need, but only for 0 value:

type MyEnum =
    | None = 0
    | Foo = 1

[<Literal>]
let X = MyEnum()
like image 30
bytebuster Avatar answered Sep 30 '22 04:09

bytebuster