Как перечислить дискриминированный союз в F #? - PullRequest
30 голосов
/ 09 августа 2011

Как я могу перечислить через возможные "значения" дискриминируемого союза в F #?

Я хочу знать, есть ли что-то вроде Enum.GetValues(Type) для дискриминируемых союзов, хотя я не уверен в том, какого родаданных я бы перечислил.Я хотел бы создать список или массив различенного объединения с одним элементом для каждой опции.

Ответы [ 4 ]

37 голосов
/ 09 августа 2011

Да, 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
9 голосов
/ 09 августа 2011

Чтобы немного расширить пример Роберта - даже если у вас нет экземпляра распознаваемого объединения, вы можете использовать отражение F #, чтобы получить информацию о типе (например, типах * 1004).* аргументов отдельных случаев).Следующий пример расширяет пример Роберта и выводит типы аргументов:

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 ранее в этом году .

6 голосов
/ 12 февраля 2015

Если ваш дискриминируемый союз состоит только из простых идентификаторов (ни в коем случае не хранятся какие-либо данные, это может быть то, что вам нужно: 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
*)
2 голосов
/ 09 августа 2011

Трудно понять, как это могло бы работать без наличия экземпляра, поскольку профсоюзы дискриминации могут нести ценности.

Если у вас был такой тип, например:

type Status = Success of string | Error of System.Exception | Timeout

Что бы вы, кроме массива, содержали для успеха или ошибки в этом случае?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...