I was writing some F# code this week and ran into problem. Consider the following code:
type Thingey = This | That | SomethingElse
Which looks like an enum. So I assumed that, like things inheriting from System.Enum, an instance of the type had a ToString method which did the right thing. But, alas, when I type it into the F# Interactive Console I get:
val it : string = "FSI_0002+Thingey"
The reason being is that the type Thingey is actually a Discriminated Union. In F#, to produce an Enumeration type you use the same syntax except you must explicitly provide an integer value for each member. Discriminated Unions can do a lot more than Enums in F#, but there are situations in which to use both.
The following snippet shows you how to use Enums and simple Discriminated Union types in F#.
// Enum type.
type FruitEnum =
| Apple = 0
| Pear = 1
| Orange = 2
| Bananna = 3
// Enums have nice built in methods to parse and get their values.
// Note the "typeof<FruitEnum>" syntax to get get the typeof an F# object.
let favoriteFruit = Enum.Parse(typeof<FruitEnum>, "Pear")
let allFruit = Enum.GetValues(typeof<FruitEnum>)
// You can even do some pattern matching...
let isGreen (day : FruitEnum) =
match day with
| FruitEnum.Apple | FruitEnum.Pear -> true
| _ -> false
// Consider the following. Leads to undefined results, but
// is valid! This is because any int can be cast to/from an
isGreen (Enum.of_int 99999)
// Now consider Discriminated Unions. One of their nice features is that the compiler can enforce
// some nice constraints when pattern matching; such as incomplete or too complete matchings.
type Shape =
// Warning: too few items. Pattern Matching incomplete.
let isRound x =
match x with
| Circle -> true
| Square -> false
// Warning FS0025: Incomplete pattern matches on this expression.
// Warning: too many items. (Matches more than every item.)
let isSquare x =
| Circle -> false
| Square -> true
| Triangle -> false
// Warning FS0026: This rule will never be matched.