Union rules solve the same problem that polymorphism solves in general: how to write generic code that operates on types not known about at the time of writing.
For example, Pants has many generic goals like `lint
` and `test
`. Those `@goal_rule
` definitions cannot know about every concrete linter or test implementation ahead-of-time.
Unions allow a specific linter to be registered with `UnionRule(LintRequest, ShellcheckRequest)
`, and then for `lint.py
` to access its type:
This example will find all registered linter implementations by looking up `union_membership[LintRequest]
`, which returns a tuple of all `LintRequest
` types that were registered with a `UnionRule
`, such as `ShellcheckRequest
` and `Flake8Request
`.
## How to create a new Union
To set up a new union, create a class for the union "base". Typically, this should be an [abstract class](🔗) that is subclassed by the union members, but it does not need to be. Mark the class with `@union
`.
Then, register every implementation of your union with `UnionRule
`:
Now, your rules can request `UnionMembership
` as a parameter in the `@rule
`, and then look up `union_membership[Vehicle]
` to get a tuple of all relevant types that are registered via `UnionRule
`.