Description
Diffo already has inherited_place and inherited_party DSL declarations (and their backing Diffo.Provider.Calculations.InheritedPlace / InheritedParty calc modules) that bring up PlaceRef / PartyRef records from instances reached by traversing AssignmentRelationship by alias. The sibling for typed characteristics is missing.
The use case showed up in diffo_example#10 (the turtles-down direction — a path bringing up its card's and shelf's typed characteristics via the port-then-slot assignment chain) and diffo_example#32 (the turtles-up direction — a shelf bringing up its cards' typed characteristics via outgoing slot assignments). We wrote both calculations locally in diffo_example and they work; they belong upstream.
What we'd find useful
Two new DSL declarations inside characteristics do, mirroring the existing inherited_place and inherited_party:
inherited_characteristic :role, via: [...] — generate a calc that traverses incoming AssignmentRelationship by alias (this instance is the target) to reach source instances, then reads the typed characteristic value declared with the given role on each source.
reverse_inherited_characteristic :role, alias: ..., characteristic: ... — generate a calc that traverses outgoing AssignmentRelationship (optionally filtered by alias) to reach assignee instances, then reads the typed characteristic value on each. Useful when the assigner wants to compose its assignees into its own view.
Backing calc modules (Diffo.Provider.Calculations.InheritedCharacteristic and Diffo.Provider.Calculations.ReverseInheritedCharacteristic) take the same via: / alias: semantics as the existing inherited-place/party calcs and the FieldVia* family.
Why it matters
Typed characteristics are how Diffo 0.4.0 expresses structured per-instance values; bringing them up across the assignment graph is the live-characteristics promise made concrete. Without an upstream primitive, every consumer writes the same calc (we wrote two flavours) — declaring the relationship is enough, the consumer shouldn't have to wire the calc logic. The pattern is identical to what inherited_place / inherited_party do for the universal Place/Party resources; the only structural difference is that typed characteristics are per-resource (e.g. ShelfCharacteristic, CardCharacteristic), so the backing calc needs to know which characteristic module to query — addressable via the role name in the DSL plus the resource's existing characteristic :role, Module declaration on each source.
This also closes the turtles-all-the-way-up/down loop from diffo_example#10: each instance shows itself, brings up its immediate-below (or immediate-above), and the chain composes naturally because every step is the same primitive.
A possible direction
Our local implementations live at:
Both mirror the shape of Diffo.Provider.Calculations.InheritedPlace — same traversal mechanics, just the final read targets the typed characteristic resource. Each is around 30-40 lines.
The DSL side could reuse the existing inherited_place / inherited_party transformer pattern in TransformInheritedRefs (or similar). The inherited_characteristic declaration would need to resolve role → typed characteristic module via the source's characteristics() declaration; for our local version we passed characteristic_module: explicitly, but the DSL macro can hide that by looking it up at compile time.
Happy to move our local implementations upstream if the shape is welcome. Related: diffo_example#10, diffo_example#32.
Description
Diffo already has
inherited_placeandinherited_partyDSL declarations (and their backingDiffo.Provider.Calculations.InheritedPlace/InheritedPartycalc modules) that bring upPlaceRef/PartyRefrecords from instances reached by traversingAssignmentRelationshipby alias. The sibling for typed characteristics is missing.The use case showed up in diffo_example#10 (the turtles-down direction — a path bringing up its card's and shelf's typed characteristics via the port-then-slot assignment chain) and diffo_example#32 (the turtles-up direction — a shelf bringing up its cards' typed characteristics via outgoing slot assignments). We wrote both calculations locally in diffo_example and they work; they belong upstream.
What we'd find useful
Two new DSL declarations inside
characteristics do, mirroring the existinginherited_placeandinherited_party:inherited_characteristic :role, via: [...]— generate a calc that traverses incomingAssignmentRelationshipby alias (this instance is the target) to reach source instances, then reads the typed characteristic value declared with the given role on each source.reverse_inherited_characteristic :role, alias: ..., characteristic: ...— generate a calc that traverses outgoingAssignmentRelationship(optionally filtered by alias) to reach assignee instances, then reads the typed characteristic value on each. Useful when the assigner wants to compose its assignees into its own view.Backing calc modules (
Diffo.Provider.Calculations.InheritedCharacteristicandDiffo.Provider.Calculations.ReverseInheritedCharacteristic) take the samevia:/alias:semantics as the existing inherited-place/party calcs and the FieldVia* family.Why it matters
Typed characteristics are how Diffo 0.4.0 expresses structured per-instance values; bringing them up across the assignment graph is the live-characteristics promise made concrete. Without an upstream primitive, every consumer writes the same calc (we wrote two flavours) — declaring the relationship is enough, the consumer shouldn't have to wire the calc logic. The pattern is identical to what
inherited_place/inherited_partydo for the universal Place/Party resources; the only structural difference is that typed characteristics are per-resource (e.g.ShelfCharacteristic,CardCharacteristic), so the backing calc needs to know which characteristic module to query — addressable via the role name in the DSL plus the resource's existingcharacteristic :role, Moduledeclaration on each source.This also closes the turtles-all-the-way-up/down loop from diffo_example#10: each instance shows itself, brings up its immediate-below (or immediate-above), and the chain composes naturally because every step is the same primitive.
A possible direction
Our local implementations live at:
lib/diffo_example/calculations/inherited_characteristic.exlib/diffo_example/calculations/reverse_inherited_characteristic.exBoth mirror the shape of
Diffo.Provider.Calculations.InheritedPlace— same traversal mechanics, just the final read targets the typed characteristic resource. Each is around 30-40 lines.The DSL side could reuse the existing
inherited_place/inherited_partytransformer pattern inTransformInheritedRefs(or similar). Theinherited_characteristicdeclaration would need to resolve role → typed characteristic module via the source'scharacteristics()declaration; for our local version we passedcharacteristic_module:explicitly, but the DSL macro can hide that by looking it up at compile time.Happy to move our local implementations upstream if the shape is welcome. Related: diffo_example#10, diffo_example#32.