Suppose you are writing some F# code to model the workings of a coffee shop. You might start by defining a small choice of snacks:
type Cost = int type Muffin = | RaspberryAndWhiteChocolate of Cost | Blueberry of Cost [<EntryPoint>] let main argv = // No need for "Muffin." qualifier on Blueberry let muffin = Blueberry 250 printfn "%A" muffin 0
This compiles and runs just fine – notice that there’s no requirement to prefix the union case “Blueberry” with the type name “Muffin”. All is well.
However, suppose you then proceed to define some more types for coffee:
type Milk = | Skimmed | Whole | SemiSkimmed type Modifier = | Milk of Milk | Cream | None // <--- Oops, clashes with FSharpOption.None type Coffee = | Filter of Modifier | Cappuccino | Latte [<EntryPoint>] let main argv = let muffin = Blueberry 250 let coffee = Filter( None ) printfn "%A %A" muffin coffee 0
This still compiles and runs – but there’s a lurking problem. As soon as you add the use of optional values, the program fails to compile. For example, you might offer free coffee as a promotion, and model the cost as optional:
let coffee = Filter( None ) let cost : int option = None
The problem is that None is now used as both a union case in Modifier and a union case in FSharpOption.
Whilst you could choose to workaround this by changing the “None” union case in Modifier to “Nothing”, you may not have that flexibility. Instead, you can use the RequireQualifiedAccess attribute on the Modifier type to stop union case names leaking into the surrounding scope:
[<RequireQualifiedAccess>] type Modifier = | Milk of Milk | Cream | None [<EntryPoint>] let main argv = let muffin = Blueberry 250 // still no qualifier required let coffee = Filter( Modifier.None ) // now requires qualifier let cost : double option = None // ok, uses FSharpOption.None printfn "%A %A %A" muffin coffee cost 0