Подтвердить что ты не робот

Как перечислить дискриминационный союз в F #?

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

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

4b9b3361

Ответ 1

Да, у 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

Ответ 2

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

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 в начале этого года.

Ответ 3

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

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

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

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

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