Designing the Field Component
Designing the Property Type Component and Designing the Module Component both called for a mechanism to handle fields.
What is the Field Component?
Modules are meant to be black-box functions which take all the input (inputs and callbacks) they will need to compute their results. The inputs and results are the fields. Since callbacks are passed as inputs we consider how they are passed to be part of the field component, while the actual callback mechanism falls to the call graph component (see Designing the Call Graph Component).
Field Component Considerations
Work with arbitrary types.
Downstream users of PluginPlay should be able to use their native types.
Recognize differences between inputs and results.
Inputs can be by value or by read-only reference.
Results are always by value.
Note can move into a value argument.
Record provenance of inputs/results.
The inputs/results should hold metadata to sufficiently describe them.
Metadata can conceivably be used by callers of the module as a “tooltip”
Verify inputs satisfy bound constraints.
Associating checks with the field avoids waiting until the module runs to ensure the field’s value is valid.
Used to prevent users from setting a field to an incorrect value (before the module tries to unwrap the input).
Leverage metadata to generate documentation.
Keeping documentation up to date with API changes can be tedious.
Generating documentation from metadata keeps code and documentation synched.
Field Component Design
Fig. 9 shows the architecture of the field component.
The three major components, ModuleInput
, ModuleResult
, and
SubmoduleRequest
correspond to three major items passed into/from modules.
ModuleInput
manages an input to a module (the set of inputs passed to a
module will be a container of ModuleInput
objects). In addition to the
type-erased input, which lives in the AnyField
object, the ModuleInput
also holds metadata (e.g., a description, if the user set the value, is
the value Opaque) and any bounds checks (e.g., can the value be null,
is there a maximum/minimum for the value, what are the allowed strings). The
ModuleResult
is similar except it does not have bounds checks (the
module developer presumably is not trying to return invalid results). The
SubmoduleRequest
class is designed to hold a callback hook, and the callback
once it is set.
Summary
Work with arbitrary types.
AnyField
uses type-erasure to wrap whatever type the module developer and/or property type developer wants.Types live in the module implementation or in the property type, both of which live downstream from PluginPlay.
Recognize differences between inputs and results.
ModuleInput
andModuleResult
are different classes to better capture this point.
Record provenance of inputs/results.
All classes have metadata to record provenance.
Verify inputs satisfy bound constraints.
ModuleInput
has the ability to store bounds checks the input values must satisfy.
Leverage metadata to generate documentation.
Not strictly part of the field component, but the field component exposes enough information for another component to generate the documentation.