Create Discriminated Union Case from String

僤鯓⒐⒋嵵緔 提交于 2019-12-17 16:28:16

问题


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
    ] 

回答1:


I found this handy code snippet...

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 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



回答2:


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 auto generate the match statement first time using regex in my editor, but only because you have hundreds of cases. But i am not sure if its a better solution then yours.




回答3:


As the cases have no value, 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 simply use Enum.Parse.



来源:https://stackoverflow.com/questions/21559497/create-discriminated-union-case-from-string

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!