Reputation: 378
I have a finite set of things all of the same type, and I wish to represent them in a strongly-typed way. I'd like to be able to manipulate the complete set and easily extract the elements. Here is one way:
type Planet = Mercury | Venus | Earth
type PlanetInfo = { Diameter: float }
let planets =
Map [ Mercury, { Diameter = 100. }
Venus, { Diameter = 200. }
Earth, { Diameter = 300. } ]
let venusDiameter = planets.[Venus].Diameter
The good points about this method are:
Planet
s, as defined by the discriminated union.planets
, which can be manipulated, iterated etc..planets.[Mars]
would cause an error, because "Mars" is not a Planet
.But on the downside:
type Planet = { Name: string; Diameter: float }
let planets =
[ { Name = "Mercury"; Diameter = 100. }
{ Name = "Venus"; Diameter = 200. }
{ Name = "Earth"; Diameter = 300. } ]
|> List.map (fun e -> e.Name, e)
|> Map
let venusDiameter = planets.["Venus"].Diameter
So now each planet is mentioned in only one place, but planets.["Mars"]
fails to cause a compile-time error because the planet identifiers are now "stringly typed".
Is there some way of doing this which has all four good points?
Upvotes: 1
Views: 88
Reputation: 1941
Another option would be to use the Planet type as the Name member in the PlanetInfo type and initialize the Map using a transformation from list:
module Planets =
type Planet =
| Mercury
| Venus
| Earth
type PlanetInfo = { Name: Planet; Diameter: float}
let planets : PlanetInfo list =
[
{Name = Mercury; Diameter = 100.}
{Name = Venus; Diameter = 200.}
{Name = Earth; Diameter = 300.}
]
let planetsmap = planets |> List.map (fun pi -> pi.Name, pi) |> Map.ofList
planetsmap.[Mercury].Diameter
This approach doesn't require reflection and offers compile time type checking. So it is pretty much the same as your second approach, Monica.
Upvotes: 3
Reputation: 2312
How about this?
type Planet =
|Mercury
|Venus
|Earth
member this.Diameter =
match this with
|Mercury -> 100.
|Venus -> 200.
|Earth -> 300.
open Microsoft.FSharp.Reflection
let planets =
FSharpType.GetUnionCases(typeof<Planet>)
|> Array.map (fun case -> FSharpValue.MakeUnion(case, [||]) :?> Planet)
Upvotes: 2