Union and generic types
MkUnion will generate generic unions for you.
For example, let's say you want to create a recursive tree data structure, that in its leaves will hold a value of A
type.
Declaration and generation
You can use mkunion
to create a union type for the tree:
//go:tag mkunion:"Tree[A]"
type (
Branch[A any] struct{ L, R Tree[A] }
Leaf[A any] struct{ Value A }
)
After you run generation (as described in getting started), you have access to the same features as with non-generic unions.
When defining generic unions, you must follow these requirements:
- Type parameters must be specified in the tag: The union tag must include all type parameters used by the variant types.
- Parameter names must match: Type parameter names in the tag must match those used in variant types both by name and position.
- Same number of parameters: Each variant type needs to have the same number of type parameters.
Matching function
Let's define a higher-order function ReduceTree
that will traverse leaves in Tree
and produce a single value.
This function uses MatchTreeR1
function that is generated automatically for you.
func ReduceTree[A, B any](x Tree[A], f func(A, B) B, init B) B {
return MatchTreeR1(
x,
func(x *Branch[A]) B {
return ReduceTree(x.R, f, ReduceTree(x.L, f, init))
}, func(x *Leaf[A]) B {
return f(x.Value, init)
},
)
}
Example usage
You can use such function to sum all values in the tree, assuming that tree is of type Tree[int]
:
func Example_treeSumValues() {
tree := &Branch[int]{
L: &Leaf[int]{Value: 1},
R: &Branch[int]{
L: &Branch[int]{
L: &Leaf[int]{Value: 2},
R: &Leaf[int]{Value: 3},
},
R: &Leaf[int]{Value: 4},
},
}
result := ReduceTree(tree, func(x int, agg int) int {
return agg + x
}, 0)
fmt.Println(result)
// Output: 10
}
You can also reduce the tree to a complex structure, for example, to keep track of the order of values in the tree, along with the sum of all values in the tree.
type orderAgg struct {
Order []int
Result int
}
func Example_treeCustomReduction() {
tree := &Branch[int]{
L: &Leaf[int]{Value: 1},
R: &Branch[int]{
L: &Branch[int]{
L: &Leaf[int]{Value: 2},
R: &Leaf[int]{Value: 3},
},
R: &Leaf[int]{Value: 4},
},
}
result := ReduceTree(tree, func(x int, agg orderAgg) orderAgg {
return orderAgg{
Order: append(agg.Order, x),
Result: agg.Result + x,
}
}, orderAgg{
Order: []int{},
Result: 0,
})
fmt.Println(result.Order)
fmt.Println(result.Result)
// Output: [1 2 3 4]
// 10
}
Next steps
- Composability and Type Safety - Learn how to compose
Option[T]
andResult[T, E]
types (Advanced topic). - Custom Pattern Matching - Learn about custom pattern matching
- Marshaling union in JSON - Learn about marshaling and unmarshalling of union types in JSON
- State Machines and unions - Learn about modeling state machines and how union type helps