Если вы хотите написать общий код обработки объединения, который не должен будет перечислять все случаи объединения, то вам, вероятно, потребуется использовать API-интерфейс отражения F #.Вот простой пример.
Функция formatUnion
использует отражение F #.Предполагается, что параметр типа 'T
является типом объединения и использует GetUnionFields
для получения кортежа, содержащего имя текущего регистра и аргументы.Он печатает текущее имя дела и перебирает все аргументы.Если некоторые из аргументов имеют значение типа 'T
(что означает, что это рекурсивное объединение), мы рекурсивно выводим информацию о значении:
let rec formatUnion indent (value:'T) = //'
// Get name and arguments of the current union case
let info, args = Reflection.FSharpValue.GetUnionFields(value, typeof<'T>) //'
// Print current name (with some indentation)
printfn "%s%s" indent info.Name
for a in args do
match box a with
| :? 'T as v ->
// Recursive use of the same union type..
formatUnion (indent + " ") v
| _ -> ()
В следующем примере функция выполняется с очень простым значением объединения:
type Element = | Nil | And of Element * Element | Or of Element * Element
formatUnion "" (And(Nil, Or(Nil, Nil)))
// Here is the expected output:
// And
// Nil
// Or
// Nil
// Nil
В качестве дополнительного примечания, я думаю, что вы могли бы значительно упростить свой дискриминируемый союз, имея регистры для BinaryOperator
и UnaryOperator
(с одним дополнительным параметром) вместо перечисления всех типов элементовв явном виде.Тогда вы, вероятно, могли бы реализовать функцию напрямую, потому что это было бы довольно просто.Что-то вроде:
type BinaryOperator = LogicalOr | LogicalAnd | BitwiseOr // ...
type UnaryOperator = Statement | Block | Initializer // ...
type Element =
| BinaryOperator of BinaryOperator * Element * Element
| UnaryOperator of UnaryOperator * Element