|
| 1 | +# Traits: compile-time properties |
| 2 | + |
| 3 | +```@meta |
| 4 | +CurrentModule = CTBase |
| 5 | +``` |
| 6 | + |
| 7 | +[`CTBase.Traits`](@ref CTBase.Traits) provides a small set of **compile-time |
| 8 | +traits** shared across the control-toolbox ecosystem. A trait is an abstract type |
| 9 | +used as a **type parameter** or returned by an accessor function so that |
| 10 | +behaviour can be selected by dispatch, with **no runtime cost**. |
| 11 | + |
| 12 | +```@repl traits |
| 13 | +using CTBase |
| 14 | +import CTBase.Traits |
| 15 | +``` |
| 16 | + |
| 17 | +A typical use case is encoding a property of a callable object — does it take a |
| 18 | +time argument? does it depend on an extra variable? is it evaluated in-place? — |
| 19 | +so that a wrapper type can select the correct call path at compile time, without |
| 20 | +runtime conditionals. |
| 21 | + |
| 22 | +## Trait families |
| 23 | + |
| 24 | +### Time dependence |
| 25 | + |
| 26 | +Does the object depend on time ``t``? |
| 27 | + |
| 28 | +| Type | Meaning | |
| 29 | +|---|---| |
| 30 | +| [`Traits.Autonomous`](@ref CTBase.Traits.Autonomous) | ``t`` is not an argument | |
| 31 | +| [`Traits.NonAutonomous`](@ref CTBase.Traits.NonAutonomous) | ``t`` must be supplied | |
| 32 | + |
| 33 | +Both are abstract subtypes of [`Traits.TimeDependence`](@ref CTBase.Traits.TimeDependence). |
| 34 | +Because they are abstract, they can only appear as type parameters — they are not |
| 35 | +instantiated, only dispatched upon. |
| 36 | + |
| 37 | +```@repl traits |
| 38 | +Traits.Autonomous <: Traits.TimeDependence |
| 39 | +Traits.NonAutonomous <: Traits.TimeDependence |
| 40 | +``` |
| 41 | + |
| 42 | +### Variable dependence |
| 43 | + |
| 44 | +Does the object depend on an extra parameter ``v`` (e.g. a free final time or a |
| 45 | +design variable)? |
| 46 | + |
| 47 | +| Type | Meaning | |
| 48 | +|---|---| |
| 49 | +| [`Traits.Fixed`](@ref CTBase.Traits.Fixed) | no ``v`` argument | |
| 50 | +| [`Traits.NonFixed`](@ref CTBase.Traits.NonFixed) | ``v`` must be supplied | |
| 51 | + |
| 52 | +Both are concrete subtypes of [`Traits.VariableDependence`](@ref CTBase.Traits.VariableDependence): |
| 53 | + |
| 54 | +```@repl traits |
| 55 | +Traits.Fixed <: Traits.VariableDependence |
| 56 | +Traits.NonFixed <: Traits.VariableDependence |
| 57 | +``` |
| 58 | + |
| 59 | +### Mutability |
| 60 | + |
| 61 | +Does a function allocate a new output, or write into a pre-allocated buffer? |
| 62 | + |
| 63 | +| Type | Meaning | |
| 64 | +|---|---| |
| 65 | +| [`Traits.OutOfPlace`](@ref CTBase.Traits.OutOfPlace) | returns a new value | |
| 66 | +| [`Traits.InPlace`](@ref CTBase.Traits.InPlace) | writes into a buffer (first arg) | |
| 67 | + |
| 68 | +### Other families |
| 69 | + |
| 70 | +These traits encode properties that are fixed at construction time and carried |
| 71 | +as type parameters. They are not opted into via the two-method contract described |
| 72 | +below — they are passed directly as type arguments. |
| 73 | + |
| 74 | +| Family | Values | |
| 75 | +|---|---| |
| 76 | +| Integration mode | [`Traits.EndPointMode`](@ref CTBase.Traits.EndPointMode), [`Traits.TrajectoryMode`](@ref CTBase.Traits.TrajectoryMode) | |
| 77 | +| Dynamics | [`Traits.StateDynamics`](@ref CTBase.Traits.StateDynamics), [`Traits.HamiltonianDynamics`](@ref CTBase.Traits.HamiltonianDynamics), [`Traits.AugmentedHamiltonianDynamics`](@ref CTBase.Traits.AugmentedHamiltonianDynamics) | |
| 78 | +| Automatic differentiation | [`Traits.WithAD`](@ref CTBase.Traits.WithAD), [`Traits.WithoutAD`](@ref CTBase.Traits.WithoutAD) | |
| 79 | +| Variable costate | [`Traits.SupportsVariableCostate`](@ref CTBase.Traits.SupportsVariableCostate), [`Traits.NoVariableCostate`](@ref CTBase.Traits.NoVariableCostate) | |
| 80 | + |
| 81 | +## The trait contract |
| 82 | + |
| 83 | +A type opts in to a trait by implementing **two methods**: one declaring that it |
| 84 | +*has* the trait, and one returning the trait *value*. The boolean predicates |
| 85 | +([`Traits.is_autonomous`](@ref CTBase.Traits.is_autonomous), |
| 86 | +[`Traits.is_variable`](@ref CTBase.Traits.is_variable), …) then follow |
| 87 | +generically — they are not implemented per type. |
| 88 | + |
| 89 | +For time and variable dependence: |
| 90 | + |
| 91 | +```@repl traits |
| 92 | +struct MyObject end |
| 93 | +
|
| 94 | +Traits.has_time_dependence_trait(::MyObject) = true |
| 95 | +Traits.time_dependence(::MyObject) = Traits.NonAutonomous |
| 96 | +
|
| 97 | +Traits.has_variable_dependence_trait(::MyObject) = true |
| 98 | +Traits.variable_dependence(::MyObject) = Traits.Fixed |
| 99 | +
|
| 100 | +obj = MyObject() |
| 101 | +Traits.is_autonomous(obj) |
| 102 | +Traits.is_nonautonomous(obj) |
| 103 | +Traits.is_variable(obj) |
| 104 | +Traits.is_nonvariable(obj) |
| 105 | +``` |
| 106 | + |
| 107 | +For mutability, the same pattern applies with `has_mutability_trait` and `mutability`: |
| 108 | + |
| 109 | +```@repl traits |
| 110 | +struct MyMutableObject end |
| 111 | +
|
| 112 | +Traits.has_mutability_trait(::MyMutableObject) = true |
| 113 | +Traits.mutability(::MyMutableObject) = Traits.InPlace |
| 114 | +
|
| 115 | +Traits.is_inplace(MyMutableObject()) |
| 116 | +Traits.is_outofplace(MyMutableObject()) |
| 117 | +``` |
| 118 | + |
| 119 | +If a type does not declare a trait, the predicates throw an informative error |
| 120 | +rather than returning a wrong default: |
| 121 | + |
| 122 | +```@repl traits |
| 123 | +try # hide |
| 124 | +Traits.is_autonomous(3.14) |
| 125 | +catch e # hide |
| 126 | +showerror(IOContext(stdout, :color => false), e) # hide |
| 127 | +end # hide |
| 128 | +``` |
| 129 | + |
| 130 | +!!! note "Trait types are shared" |
| 131 | + Because trait types (e.g. `Traits.Autonomous`) are defined in a single place, |
| 132 | + a type that carries `Traits.Autonomous` as a type parameter and an object that |
| 133 | + implements `time_dependence` returning `Traits.Autonomous` are compatible by |
| 134 | + construction — no conversion or mapping needed. |
| 135 | + |
| 136 | +## Accessor / predicate summary |
| 137 | + |
| 138 | +| Trait value function | Boolean predicates | |
| 139 | +|---|---| |
| 140 | +| [`Traits.time_dependence`](@ref CTBase.Traits.time_dependence) | `is_autonomous`, `is_nonautonomous` | |
| 141 | +| [`Traits.variable_dependence`](@ref CTBase.Traits.variable_dependence) | `is_variable`, `is_nonvariable`, `has_variable` | |
| 142 | +| [`Traits.mutability`](@ref CTBase.Traits.mutability) | `is_inplace`, `is_outofplace` | |
| 143 | +| [`Traits.ad_trait`](@ref CTBase.Traits.ad_trait) | — | |
| 144 | +| [`Traits.variable_costate_trait`](@ref CTBase.Traits.variable_costate_trait) | — | |
| 145 | +| [`Traits.dynamics_trait`](@ref CTBase.Traits.dynamics_trait) | — | |
| 146 | + |
| 147 | +## See Also |
| 148 | + |
| 149 | +- [Exceptions guide](exceptions.md) — understanding `IncorrectArgument` and `NotImplemented`. |
0 commit comments