Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider separating "forcing" from explicit-Rep #122

Open
chiphogg opened this issue Mar 14, 2023 · 2 comments
Open

Consider separating "forcing" from explicit-Rep #122

chiphogg opened this issue Mar 14, 2023 · 2 comments
Labels
⬇️ affects: code (interfaces) Affects the way end users will interact with the library 📁 kind: enhancement New feature or request 💪 effort: large

Comments

@chiphogg
Copy link
Contributor

I have always conflated these ideas, probably because static_cast does both. But it could be nice to be able to write, say, inches(50).force_as(feet): we wouldn't need to repeat the Rep, and the "force" makes the potential lossiness more obvious. This would free up q.as<T>(unit) to respect the safety checks.

@chiphogg chiphogg added 📁 kind: enhancement New feature or request 💪 effort: epic Major features that will take considerable planning and effort to add labels Mar 14, 2023
@chiphogg chiphogg added 💪 effort: large ⬇️ affects: code (interfaces) Affects the way end users will interact with the library and removed 💪 effort: epic Major features that will take considerable planning and effort to add labels Mar 26, 2023
@chiphogg chiphogg added this to the 0.4.0 milestone Jun 21, 2023
@chiphogg
Copy link
Contributor Author

On further reflection and discussion, I'm leaning towards coerce_as rather than force_as, since "force" already has a different meaning in units-land.

chiphogg added a commit that referenced this issue Aug 29, 2023
These are variants of `.as` and `.in` for both `Quantity` and
`QuantityPoint`.  When we precede these with the "coerce" vocabulary
word, they become forcing, ignoring the safety checks for truncation and
overflow.

This new syntax has two major advantages over the explicit-Rep version.
First, it makes the intent clearer.  Second, it stops forcing users to
repeat the rep when they want the same rep they started with, which I
think is the usual case.  (Thus, without these APIs, explicit-rep
obscures intent: one never knows whether the author wanted a cast, or
wanted to bypass the safety checks.)

It also unlocks another, later improvement: we will be able to extend
the safety checks to the explicit-rep versions!  But we won't attempt
that for a while because that's a breaking change.

There may be other APIs that would benefit from using the "coerce"
vocabulary word instead of explicit-rep.  However, we'll start with just
these, both because they're the overwhelmingly most common use case, and
because it gives us a chance to try out these ideas in practice for a
while.

Helps #122.

We'll start with just these functions because they're by far the most
prevalent.  Later on, we can
chiphogg added a commit that referenced this issue Sep 1, 2023
These are variants of `.as` and `.in` for both `Quantity` and
`QuantityPoint`.  When we precede these with the "coerce" vocabulary
word, they become forcing, ignoring the safety checks for truncation and
overflow.

This new syntax has two major advantages over the explicit-Rep version.
First, it makes the intent clearer.  Second, it stops forcing users to
repeat the rep when they want the same rep they started with, which I
think is the usual case.  (Thus, without these APIs, explicit-rep
obscures intent: one never knows whether the author wanted a cast, or
wanted to bypass the safety checks.)

It also unlocks another, later improvement: we will be able to extend
the safety checks to the explicit-rep versions!  But we won't attempt
that for a while because that's a breaking change.

There may be other APIs that would benefit from using the "coerce"
vocabulary word instead of explicit-rep.  However, we'll start with just
these, both because they're the overwhelmingly most common use case, and
because it gives us a chance to try out these ideas in practice for a
while.

To test this PR, I added new unit tests to cover all of the use cases.
I also rendered the docs locally and checked that the links worked as
intended.

Helps #122.
@chiphogg
Copy link
Contributor Author

chiphogg commented Feb 9, 2025

I woke up this morning with an exciting new idea for APIs. Writing it down now so I can get to it later.

  • Make an enum class, RiskCheck, with four values, NONE (00b), OVERFLOW (01b), TRUNCATION (10b), ALL (11b). Note how it automatically acts as a bitmask.
  • Make a constant type, templated on RiskCheck, and make four pre-made values:
constexpr auto SKIP_ALL_CHECKS = RiskPolicy<RiskCheck::NONE>;
constexpr auto SKIP_TRUNCATION_RISK_CHECK = RiskPolicy<RiskCheck::OVERFLOW>;
constexpr auto SKIP_OVERFLOW_RISK_CHECK = RiskPolicy<RiskCheck::TRUNCATION>;
constexpr auto PERFORM_ALL_CHECKS = RiskPolicy<RiskCheck::ALL>;

I think expressing the callsite in terms of which check we are explicitly skipping will better communicate intent.

  • Add a second argument to .in and .as.
  • Make the single-argument .in and .as delegate to the appropriate overload: SKIP_ALL_CHECKS for explicit-rep (for now!), and PERFORM_ALL_CHECKS for implicit-rep.
  • Deprecate coerce_in and coerce_as: the naming was always controversial.
  • Re-express all conversion policies in terms of separate checks for overflow and truncation.
  • The two-argument form of .in and .as will call out to the appropriate specializations for these separated conversion policies, depending on the second argument.

Benefits:

  • This is a totally new signature, so people can adopt it without forcing upgrades (due to giving an old signature a new meaning, as we hope to do with explicit-rep).
  • Far easier upgrade path for explicit-rep APIs
  • Much better at expressing intent: some embedded use cases, for example, explicitly want to truncate, and these callsites would be much more self-documenting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
⬇️ affects: code (interfaces) Affects the way end users will interact with the library 📁 kind: enhancement New feature or request 💪 effort: large
Projects
None yet
Development

No branches or pull requests

1 participant