Как перечислить дискриминированный союз в F#?
Как я могу перечислить через возможные "значения" различаемого объединения в F#?
Я хочу знать, если есть что-то вроде Enum.GetValues(Type)
Что касается дискриминационных союзов, то я не уверен, какие именно данные я бы перечислил. Я хотел бы создать список или массив различенного объединения с одним элементом для каждого параметра.
4 ответа
Да, F# имеет свой собственный слой отражения, построенный поверх отражения.NET, чтобы помочь вам разобраться в типах, специфичных для F#, таких как различающие объединения. Вот код, который позволит вам перечислить случаи объединения:
open Microsoft.FSharp.Reflection
type MyDU =
| One
| Two
| Three
let cases = FSharpType.GetUnionCases typeof<MyDU>
for case in cases do printfn "%s" case.Name
Если ваш дискриминируемый союз состоит только из простых идентификаторов (ни в одном случае не хранятся какие-либо данные, это может быть то, что вам нужно: 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
*)
Чтобы немного расширить пример Роберта - даже если у вас нет экземпляра дискриминируемого объединения, вы можете использовать отражение F# для получения информации о типе (например, типы аргументов отдельных случаев). Следующее расширяет образец Роберта и печатает типы аргументов:
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 ""
Например, для option<int>
типа, вы получите (я немного упростил вывод):
type Microsoft.FSharp.Core.FSharpOption`1[System.Int32] =
| None
| Some of System.Int32
Существует много интересных применений этой информации - например, вы можете сгенерировать схему БД из объединений F# или создать функции, которые будут анализировать XML в различимое объединение (которое описывает структуру). Я говорил об образце обработки XML на конференции GOTO в начале этого года.
Трудно понять, как это могло бы работать без наличия экземпляра, поскольку профсоюзы дискриминации могут нести ценности.
Если у вас был такой тип, например:
type Status = Success of string | Error of System.Exception | Timeout
Что бы вы, кроме вашего массива, содержали для успеха или ошибки в этом случае?