Feedback from building a non-physical, application-owned quantity on UnitsNet.Modular, in
case it is useful while Modular is still pre-release. This is a report, not a request — nothing
here needs an upstream change, and the probe runs against Modular as published.
Full write-up, definition and code: https://gist.github.com/48f6f6103616bcd79fa22f2bae60c7c9
What it is
A boolean/logic-state quantity where each unit is a named sense of the same bit. The case
it exists for is that standards disagree on polarity on purpose — KNX DPT 1.009 DPT_OpenClose
is 0 = open, 1 = close, while DPT 1.019 DPT_Window_Door is 0 = closed, 1 = open — so "1"
on the wire means nothing without its sense. That is a unit conversion, and 1 - {x} is the
conversion function.
73 units / 36 sense pairs, sourced per unit from KNX DPT 1.xxx, BACnet, OPC UA, IEC 61850,
IEC 61131-3, ANSI/ISA-18.2, NAMUR NE 107 and PackML. Verified on every build: 146 round-trips
over every unit with 0 failures, plus a constraint puzzle checked symbolically and against an
independently written native expression, agreeing on 32768/32768 assignments.
It generates cleanly, unchanged
Empty BaseDimensions, {x} and 1 - {x} conversions, 73 units, no warnings. The polarity
model carries the whole design: opposite-sense units are ordinary units with an inverting
conversion.
The generated struct being partial is a real extension point
The generator emits public readonly partial struct <Name>, so a consumer can declare the other
half and add what the generator does not — here &, |, ^, ~, ! and an explicit collapse
to bool, with no upstream change and no generator flag.
That is worth writing down because it answers "can Modular express a quantity with
domain-specific operators?" with yes, downstream, today. It also suggests the generator
should not emit such operators: over [0,1] the Zadeh (min/max) and probabilistic
(x*y, x+y-xy) t-norms disagree while coinciding on {0,1}, so generated operators would
have to pick one on every consumer's behalf.
The one thing that might deserve a docs line
Unit names become member names, so a consumer writing the other half of the partial is
constrained by the unit catalogue, not only by the public API. A private helper named
Valid would not compile:
error CS0102: The type 'LogicState' already contains a definition for 'Valid'
because Valid/Invalid is one of the sense pairs. Renaming fixed it, and nothing is wrong
with the generator — but "your unit names occupy the member namespace your own partial half has
to share" is not obvious until it bites, and it will bite anyone extending a generated quantity.
Happy to open a small docs PR if that seems worth having; equally happy to leave it.
Noted, not proposed
Parse("0 off") yields true. Correct by the model — 0 in the Off sense means not-off —
and still a trap for a human reading a log. KNX and BACnet solve the presentation side with
value-dependent text (active_text/inactive_text), which has no equivalent here and is
probably out of scope for a units library.
No rush on any of this. Filing it because pre-release feedback on an unusual use of the
generator seemed more useful sitting in the tracker than in a private workspace.
Feedback from building a non-physical, application-owned quantity on
UnitsNet.Modular, incase it is useful while Modular is still pre-release. This is a report, not a request — nothing
here needs an upstream change, and the probe runs against Modular as published.
Full write-up, definition and code: https://gist.github.com/48f6f6103616bcd79fa22f2bae60c7c9
What it is
A boolean/logic-state quantity where each unit is a named sense of the same bit. The case
it exists for is that standards disagree on polarity on purpose — KNX DPT 1.009
DPT_OpenCloseis
0 = open, 1 = close, while DPT 1.019DPT_Window_Dooris0 = closed, 1 = open— so "1"on the wire means nothing without its sense. That is a unit conversion, and
1 - {x}is theconversion function.
73 units / 36 sense pairs, sourced per unit from KNX DPT 1.xxx, BACnet, OPC UA, IEC 61850,
IEC 61131-3, ANSI/ISA-18.2, NAMUR NE 107 and PackML. Verified on every build: 146 round-trips
over every unit with 0 failures, plus a constraint puzzle checked symbolically and against an
independently written native expression, agreeing on 32768/32768 assignments.
It generates cleanly, unchanged
Empty
BaseDimensions,{x}and1 - {x}conversions, 73 units, no warnings. The polaritymodel carries the whole design: opposite-sense units are ordinary units with an inverting
conversion.
The generated struct being
partialis a real extension pointThe generator emits
public readonly partial struct <Name>, so a consumer can declare the otherhalf and add what the generator does not — here
&,|,^,~,!and an explicit collapseto
bool, with no upstream change and no generator flag.That is worth writing down because it answers "can Modular express a quantity with
domain-specific operators?" with yes, downstream, today. It also suggests the generator
should not emit such operators: over
[0,1]the Zadeh (min/max) and probabilistic(
x*y,x+y-xy) t-norms disagree while coinciding on{0,1}, so generated operators wouldhave to pick one on every consumer's behalf.
The one thing that might deserve a docs line
Unit names become member names, so a consumer writing the other half of the partial is
constrained by the unit catalogue, not only by the public API. A private helper named
Validwould not compile:because
Valid/Invalidis one of the sense pairs. Renaming fixed it, and nothing is wrongwith the generator — but "your unit names occupy the member namespace your own partial half has
to share" is not obvious until it bites, and it will bite anyone extending a generated quantity.
Happy to open a small docs PR if that seems worth having; equally happy to leave it.
Noted, not proposed
Parse("0 off")yields true. Correct by the model —0in theOffsense means not-off —and still a trap for a human reading a log. KNX and BACnet solve the presentation side with
value-dependent text (
active_text/inactive_text), which has no equivalent here and isprobably out of scope for a units library.
No rush on any of this. Filing it because pre-release feedback on an unusual use of the
generator seemed more useful sitting in the tracker than in a private workspace.