Create discriminated case from string

I am trying to create DU cases from strings. The only way I can do this is to list the DU cases through Microsoft.FSharp.Reflection.FSharpType.GetUnionCases , and then select UnionCase that matches the string (using .Name ), and then make the actual DU case from it using FSharpValue.MakeUnion .

Isn't there an easier / more elegant way to do this? In my scenario, I have a DU with several hundred cases for keywords. I have to read lines (keywords) from a file and make types out of them. I did some β€œoptimization” by putting matters at stake, but I was hoping there would be a better way to do 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 ] 
+7
source share
3 answers

I found this handy piece of code ...

 open Microsoft.FSharp.Reflection let toString (x:'a) = match FSharpValue.GetUnionFields(x, typeof<'a>) with | case, _ -> 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 bind to two lines of code to any DU ...

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

I would use pattern matching like this:

 type Keyword = | FOO | BAR | BAZ | BLAH let matchKeyword (word:string) : Keyword option = match word with | "FOO" -> Some FOO | "BAR" -> Some BAR | "BAZ" -> Some BAZ | "BLAH" -> Some BLAH | _ -> None 

And maybe automatically generate a match operator for the first time using a regular expression in my editor, but only because you have hundreds of cases. But I'm not sure if this is a better solution than yours.

+6
source share

Since cases do not matter, another option is to use enums :

 type Keyword = | FOO = 0 | BAR = 1 | BAZ = 2 | BLAH = 3 let strings = ["FOO";"BAR"] let keywords = [for s in strings -> s, Keyword.Parse(typeof<Keyword>, s)] |> Map.ofList 

Then you can just use Enum.Parse .

+6
source share

All Articles