C++ Logo

std-proposals

Advanced search

Re: [std-proposals] On the standardization of mp-units P3045R1

From: Charles R Hogg <charles.r.hogg_at_[hidden]>
Date: Thu, 27 Jun 2024 16:02:47 -0400
On Thu, Jun 27, 2024 at 2:52 PM Tiago Freire <tmiguelf_at_[hidden]> wrote:

> > In your specific example, this would be less likely because you've
> included the full unit name in your alias. However, not everyone wants to
> do this --- often, people want to give it a more generic alias, like
> `temperature`. Preventing this constructor from a single value (even when
> `explicit`), and making sure that every option forces the user to name the
> unit explicitly at the callsite, preserves unit safety, and reduces
> cognitive load.
>
>
>
> I think you focused too much on the alias part, while the important part
> is a fully named type. In many codes bases the rule to explicit type out
> your types are a thing.
>
> If I can fully type out the type that I want, why shouldn’t I be able to
> use it?
>
> Why exactly do I need a separate gadget to create that type?
>
> Talking about a mental effort, it is not enough to know the type that you
> want, you also need to lookup the right gadget (whose name has nothing to
> do with the type you are trying to construct) in order to construct your
> type,… what? Why?
>

Why? The principle is "unit safety" for interfaces. It's a core design
principle for units libraries, which was pioneered by the Au
<https://github.com/aurora-opensource/au> library, accepted and adopted in
mp-units, and included in P3045. The "unit safety" principle depends on
another principle, "unit correctness", which is one that people are more
familiar with. Here are the definitions taken from the Au 101 tutorial
<https://aurora-opensource.github.io/au/main/tutorial/101-quantity-makers/>:

   - *Unit Correctness:* a program is *unit-correct* when every variable
   associated with physical units is used consistently with those units.
   - *Unit Safety:* a program is *unit-safe* when the *unit-correctness* of
   each line of code can be checked by *inspection*, in *isolation*.

The reason this is desirable is because it minimizes cognitive load. A
unit-safe line does not guarantee that the program has no unit errors. It
*does* mean that if there *are* unit errors, then they are in *some other* line
(which you can also inspect). You can read a unit-safe line once, and be
done with it!

The way to achieve this with a units library is to make sure that every
interface that *brings values into* a units library type, or *extracts
values from* a units library type, forces the user to *name the unit*
explicitly,
*at the callsite*. Au was designed with this approach in mind from the
ground up, and I was very heartened to see mp-units adopt it as well, to
the point that mp-units is now every bit the equal of Au in unit safety.
(Really, the whole 2.0 redesign for mp-units was a stunning leap forward in
a lot of ways --- I was thrilled to see it.)

Now to your question: if you have a typename that contains the unit name,
isn't that enough? Say we had a *type* called `degrees_celsius`: why
wouldn't it be OK to have a constructor from `double`, so you could simply
write `degrees_celsius{42.0}`? (We would make it `explicit`, of course!)

One reason is the aliases, yes, but you've already explained why you don't
find that persuasive. I still think it's valuable to expand the universe
of safe use cases, so that people who *want* to refer to their type as
`Temperature` can safely do so, but I could also imagine you simply
proposing a rule that people should avoid such aliases. Fine.

However, even this is not enough. The "explicit is not explicit enough
<https://open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3045r1.html#explicit-is-not-explicit-enough>"
section which I linked to earlier already explains why. You could have
something like this:

std::vector<degrees_celsius> temps;

// ...

temps.emplace_back(20.0);


The `emplace_back` line does not name the unit: it's not unit safe. We
can't reason about the correctness of that line in isolation. And if you
provide an explicit single-numeric constructor, *you can't prevent* users
from calling `emplace_back` in this way.

So the solution is to provide quantity creators whose name is the name of
the unit. It's clearly hyperbole to say that the "name has nothing to do
with the type you are trying to construct": it has the same unit! This
exaggeration undercuts persuasiveness.

Ultimately, experience at scale shows that these unit-safe interfaces are
easy to teach and learn, and effective at preventing unit-correctness
errors.

Cheers,
Chip


>
>
> This doesn’t have the confusing problem that “30 * deg_C” feels wrong,
> because it is wrong, it’s not a multiplication why do you write it as a
> multiplication?
>
>
>

Received on 2024-06-27 20:03:01