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

Replace Boolean with convertible_to<bool> #389

Open
CaseyCarter opened this issue Mar 15, 2017 · 22 comments
Open

Replace Boolean with convertible_to<bool> #389

CaseyCarter opened this issue Mar 15, 2017 · 22 comments

Comments

@CaseyCarter
Copy link
Collaborator

CaseyCarter commented Mar 15, 2017

From LWG Kona review of #155. Boolean is an enormously complicated hunk of specification for what is essentially intended to allow people to return integers from comparison operators in addition to bool. LWG made the suggestion to simply define Boolean as:

template <class T>
concept bool Boolean() { return Integral<T>(); }

which achieves the same goal with two lines of spec. At the very least, it's a reasonable idea to apply this simplification for the TS and see if anyone complains.

Proposed Resolution

See P1934.

@ericniebler
Copy link
Owner

That breaks the use of (non-scoped) enums as booleans. Does that matter?

@ericniebler ericniebler self-assigned this Jun 16, 2017
@ericniebler
Copy link
Owner

Assigning to myself...

@ericniebler
Copy link
Owner

This can be implemented with something like BuiltinImplicitlyConvertibleTo<T, int>, which can be implemented with the help of the rule prohibiting conversion sequences with two user-defined conversions.

@timsong-cpp
Copy link
Contributor

Would also outlaw using std::true_type to signify that something always return true.

@ericniebler
Copy link
Owner

Would also outlaw using std::true_type to signify that something always return true.

Have you used that idiom, or seen others using it?

@timsong-cpp
Copy link
Contributor

Have you used that idiom, or seen others using it?

I recall reading that someone proposed having operator== on allocators return true_type as an alternative during the discussion that led to allocator_traits::is_always_equal. I suspect that suggestion wasn't invented on the spot.

@ericniebler
Copy link
Owner

ericniebler commented Jun 17, 2017

The following Boolean concept passes all of cmcstl2's tests unmodified:

namespace detail {
  template <class B>
  constexpr bool boolean_v =
    std::is_integral<B>::value || std::is_enum<B>::value;

  template <MoveConstructible B>
    requires requires(const B& b) { b.operator bool(); }
  constexpr bool boolean_v<B> = true;

  struct implicit_bool {
    implicit_bool(bool);
  };
}

template <class B>
concept bool Boolean =
  detail::boolean_v<decay_t<B>> &&
  requires (const decay_t<B> b) {
    { b } -> detail::implicit_bool;
  };

Thoughts?

EDIT: I observe that just because a class type provides operator bool doesn't necessarily mean the type can be used like a bool. It may = delete its operator! or its logical operators, or subvert the Boolean-ness of the type in other ways.

@timsong-cpp
Copy link
Contributor

Doesn't that still allow

enum C {};
void operator&&(C,C);

@ericniebler
Copy link
Owner

Yup.

@CaseyCarter
Copy link
Collaborator Author

I observe that just because a class type provides operator bool doesn't necessarily mean the type can be used like a bool.

This is why we have the boolean expression grammar that is the current formulation of Boolean: the fact that a type can convert to T doesn't mean that a type necessarily must convert to T and act like a T when we want it to do so. I'm beginning to think there just isn't any middle ground between the current Boolean and Integral: either we don't admit any types upon which a user can define operator overloads, and Boolean stays simple, or we do admit them and inevitably require the entire grammar to constrain the behavior of operator overloads.

Note that the current (#155) formulation is still underconstrained in that it only specifies the behavior of a single model of Boolean in expressions. "Given const lvalues b1 and b2 of type remove_reference_t<B>, then Boolean<B>() is satisfied if and only if..." says nothing about what happens in:

template<Range Rng1, Range Rng2>
bool f(Rng1&& rng1, Rng2&& rng2) {
  return begin(rng1) == end(rng1) && begin(rng2) == end(rng2);
}

where the && operator is being applied to two potentially different models of Boolean.

@ericniebler
Copy link
Owner

Maybe I'm just chicken, but this (changing Boolean to Integral) feels like too big a change to make this close to release. I feel like punting to IS. Thoughts?

@CaseyCarter
Copy link
Collaborator Author

CaseyCarter commented Jun 29, 2017

🐔!

Serious response: We don't really have an idea of the impact this will have on existing codebases that want to transition to Ranges. One way to get that idea would be to put it into the TS. A better way might be to leave the TS alone and survey users and implementers: "What impact would it have on your Ranges code if <blank happens>?"

@ericniebler
Copy link
Owner

So, leave Boolean as it currently is and get experience? My only problem with that is that going from Boolean to Integral is a tightening. And making type-checking stricter is harder than going the other direction.

@CaseyCarter
Copy link
Collaborator Author

And making type-checking stricter is harder than going the other direction.

Strengthening requirements means implementors of models have to do more work (do I meet the tighter requirements?). Weakening requirements means users of models have to do more work (are the weaker guarantees sufficient for my usage?). My hunch is that there will be more users than implementors of Boolean models so that tightening would have a lesser overall impact.

That said, our historical response to this kind of dilemma is to screw things down as tightly as possible: I'd almost prefer to require exactly bool and relax it only if people scream. I fear that "convertible-ish to bool" will lead to confusion among users who are used to "non-zero means true" when they hit our foo == bool expression requirement and discover that our version of "allowing" int here means only 0 and 1.

@ericniebler
Copy link
Owner

I'd almost prefer to require exactly bool and relax it only if people scream.

I'm way too 🐔 for that. I want Ranges TS to ship in Toronto. I can just imagine the long faces we would get if we brought forward a proposal to change -> Boolean to -> Same<bool>.

@CaseyCarter
Copy link
Collaborator Author

I want Ranges TS to ship in Toronto.

Yip. I afraid that if we slip another meeting we'll start to hear "you should rebase on C++17!" Given my belief that there's no reasonable middle ground between Integral and "ridiculous boolean expression grammar", I agree that we should push this to IS or TS2.

@CaseyCarter CaseyCarter added TS2 and removed PDTS labels Jun 30, 2017
@cjdb
Copy link
Contributor

cjdb commented Jun 30, 2017

I want Ranges TS to ship in Toronto.

Yes, please.

However, going off N4651, it appears to me that Integral only accepts fundamental integers, while Boolean accepts custom Boolean types.

What is the motivation for changing Boolean to disallow My_bool, and not changing Integral to allow My_int?

@CaseyCarter
Copy link
Collaborator Author

CaseyCarter commented Jun 30, 2017

However, going off N4651, it appears to me that Integral only accepts fundamental integers, while Boolean accepts custom Boolean types.

(We're up to N4671 as of this week's pre-meeting mailing, FYI - there are only a few editorial differences.) Yes, those are observations both correct.

What is the motivation for changing Boolean to disallow My_bool, and not changing Integral to allow My_int?

Boolean is a (misguided?) attempt to allow comparison operators to return types convertible to bool inspired by LWG 2114. We've revised it 3 or 4 times over the last two years into its current (still not exactly correct) boolean-expression-grammar-like form. I'm somewhat concerned that it is:

  • painfully awkward to specify
  • costly to concept-check

and that those prices buy us flexibility that mostly will not be used. I made a comment in Kona to the effect that I'm reasonably certain that Boolean only admits bool and integer types restricted to the domain {0, 1} (I missed enums restricted to the domain {0, 1}) and it was suggested that maybe we should simply replace Boolean with Integral.

Changing Integral would require someone to go to the trouble to characterize Integral types, documenting all of the requirements that Integral models satisfy, and deciding which of them are critical to Integral-ness. That would include obvious things like "Integral refines Regular" and more subtle things like "Integrals have only constant-time operations" and "Integrals are cheaper to pass by value than by reference," and "Integrals can be non-type template parameters." (In fairness, the last three are probably all properties that Integral types inherit from Scalar types.)

@ericniebler
Copy link
Owner

more subtle things like "Integrals have only constant-time operations" and "Integrals are cheaper to pass by value than by reference," and "Integrals can be non-type template parameters." (In fairness, the last three are probably all properties that Integral types inherit from Scalar types.)

It's not at all obvious to me that these are desirable, since it would exclude a bigint type.

@ericniebler
Copy link
Owner

I'd like to suggest another tact here. Probably my biggest problem with Boolean right now is the likely effect it has on compile times. If we permitted an implementation that dispatched to a trait -- hiding all the complexity behind a template that gets memoized -- then compile times would be vastly improved. We might even require such an implementation.

If we bring forward such a suggestion, it would be nice to have some numbers about if/how much it improves compile times.

@ericniebler ericniebler removed their assignment Apr 9, 2019
@ericniebler
Copy link
Owner

LEWG voted in Cologne to replace Boolean with convertible_to<bool>. TODO: bring a paper to Belfast.

@ericniebler ericniebler changed the title Consider reducing Boolean to Integral Replace Boolean with convertible_to<bool> Aug 13, 2019
@CaseyCarter
Copy link
Collaborator Author

LEWG voted in Cologne to replace Boolean with convertible_to<bool>.

I would characterize this as LEWG providing encouragement to bring a proposal to make such a change; this phrasing suggests that LEWG has approved making the change which they of course cannot without a proposal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants