Создать дискриминированное объединенное дело из строки

Я пытаюсь создать DU случаи из строк. Единственный способ увидеть это - перечислить случаи DU с помощью Microsoft.FSharp.Reflection.FSharpType.GetUnionCases а затем выбирая UnionCase которая соответствует строке (используя .Name), а затем сделать фактический случай DU из этого с помощью FSharpValue.MakeUnion,

Нет ли более легкого / более элегантного способа сделать это? В моем сценарии у меня есть DU с парой сотен случаев для ключевых слов. Я должен прочитать строки (ключевые слова) из файла и сделать из них типы. Я сделал некоторую "оптимизацию", поместив эти случаи в карту, но я надеялся, что будет лучший способ сделать это.

У меня есть следующее, например:

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
    ] 

3 ответа

Я нашел этот удобный фрагмент кода...

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

... что позволяет легко привязать две строки кода к любому DU...

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

Я хотел бы использовать сопоставление с образцом, как это:

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

И, возможно, автоматически сгенерировать оператор сопоставления в первый раз, используя регулярное выражение в моем редакторе, но только потому, что у вас есть сотни случаев. Но я не уверен, что это лучшее решение, чем ваше.

Поскольку регистры не имеют значения, другой вариант - использовать перечисления:

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

Тогда вы можете просто использовать Enum.Parse.

Другие вопросы по тегам