问题
How can I enumerate through the possible "values" of a discriminated union in F#?
I want to know if is there something like Enum.GetValues(Type)
for discriminated unions, tough I am not sure over what kind of data I would enumerate. I would like to generate a list or array of a discriminated union with one item for each option.
回答1:
Yes, F# has it's own reflection layer build on top of .NET's reflection to help you make sense of types that are specific to F#, like discriminating unions. Here's the code that will let you enumerate a union's cases:
open Microsoft.FSharp.Reflection
type MyDU =
| One
| Two
| Three
let cases = FSharpType.GetUnionCases typeof<MyDU>
for case in cases do printfn "%s" case.Name
回答2:
To slightly extend Robert's example - even if you don't have an instance of the discriminated union, you can use F# reflection to get the information about the type (such as types of the arguments of individual cases). The following extends Robert's sample ans it also prints the types of arguments:
open Microsoft.FSharp.Reflection
let ty = typeof<option<int>>
let cases = FSharpType.GetUnionCases ty
printfn "type %s =" ty.FullName
for case in cases do
printf "| %s" case.Name
let fields = case.GetFields()
if fields.Length > 0 then
printf " of"
for fld in fields do
printf " %s " fld.PropertyType.FullName
printfn ""
For example, for option<int>
type, you'll get (I slightly simplified the output):
type Microsoft.FSharp.Core.FSharpOption`1[System.Int32] =
| None
| Some of System.Int32
There are many interesting uses for this information - for example, you could generate DB schema from F# unions or to create functions that will parse XML into a discriminated union (that describes the structure). I talked about the XML processing sample at GOTO conference earlier this year.
回答3:
If your discriminated union is only made of plain identifiers (no case storing any data, this might be what you need: gist
open Microsoft.FSharp.Reflection
module SimpleUnionCaseInfoReflection =
// will crash if 'T contains members which aren't only tags
let Construct<'T> (caseInfo: UnionCaseInfo) = FSharpValue.MakeUnion(caseInfo, [||]) :?> 'T
let GetUnionCaseInfoAndInstance<'T> (caseInfo: UnionCaseInfo) = (caseInfo, Construct<'T> caseInfo)
let AllCases<'T> =
FSharpType.GetUnionCases(typeof<'T>)
|> Seq.map GetUnionCaseInfoAndInstance<'T>
#load "SimpleUnionCaseInfoReflection.fs"
type Foos = Foo | Bar | Baz
SimpleUnionCaseInfoReflection.AllCases<Foos> |> Seq.iter (fun (caseInfo, instance) ->printfn "name: %s instance: %O is Bar? : %b" caseInfo.Name instance (instance.Equals(Foos.Bar)))
(*
> name: Foo instance: FSI_0055+Foos is Bar? : false
> name: Bar instance: FSI_0055+Foos is Bar? : true
> name: Baz instance: FSI_0055+Foos is Bar? : false
*)
回答4:
It's hard to see how this could possibly work without also having an instance since discrimination unions can carry values.
If you had a type like this for example:
type Status = Success of string | Error of System.Exception | Timeout
What would you except your array to contain for Success or Error in this case?
来源:https://stackoverflow.com/questions/6997083/how-to-enumerate-a-discriminated-union-in-f