Create Discriminated Union Case from String

f#, f#-3.0

Solution

I found this handy code snippet...

open Microsoft.FSharp.Reflection

let toString (x:'a) = 
    let (case, _ ) = FSharpValue.GetUnionFields(x, typeof<'a>)
    case.Name

let fromString<'a> (s:string) =
    match FSharpType.GetUnionCases typeof<'a> |> Array.filter (fun case -> case.Name = s) with
    |[|case|] -> Some(FSharpValue.MakeUnion(case,[||]) :?> 'a)
    |_ -> None

... which makes it easy to tack on two lines of code to any DU...

type A = X|Y|Z with
    override this.ToString() = FSharpUtils.toString this
    static member fromString s = FSharpUtils.fromString<A> s

Problem

I'm trying to create DU cases from strings. The only way I can see doing this is by enumerating over the DU cases via `Microsoft.FSharp.Reflection.FSharpType.GetUnionCases` and then picking the `UnionCase` that matches the string (by using `.Name`) and then making the actual DU case out of that by using `FSharpValue.MakeUnion`. Isn't there an easier/more elegant way of doing this? In my scenario I have a DU with a couple of hundred cases for keywords. I have to read the strings (keywords) from a file and make the types out of them. I did some "optimization" by putting the cases into a Map but I was hoping there'd be a better way of doing this. I have the following, for example: ``` type Keyword = | FOO | BAR | BAZ | BLAH let mkKeywords (file: string) = use sr = new StreamReader(file) let caseMap = FSharpType.GetUnionCases(typeof<Keyword>) |> Array.map (fun c -> (c.Name, FSharpValue.MakeUnion(c, [||]) :?> Keyword)) |> Map.ofArray [ while not sr.EndOfStream do let l = sr.ReadLine().Trim() match caseMap.TryFind l with | Some c -> yield c | None -> failwith <| "Could not find keyword: " + l ] ```

Original source