A plea for a consistent, terse and intuitive declaration syntax

399 views
Skip to first unread message

Corentin

unread,
Nov 6, 2017, 4:27:33 PM11/6/17
to ISO C++ Standard - Future Proposals
Hello.
This proposal is a bit similar to Jakob Riedle's "Concepts are Adjectives, not Nouns" which you can read about here https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/8hmrsEfg8Oc


My goal was to have the same syntax, with the same semantic across lambda, function and templates parameters, as well as variable declarations and some other constructs.
I do that by allowing concepts ( aka constraints) to be applied to both template and non-template type, using the notation that Jakob calls "Adjectives" syntax.

Uniformity, intuitiveness and terseness were the main driving goals.



Please note that the document is not yet final, notably it is missing references and acknowledgments.


I hope to here your feedback !

Regards,  Corentin

floria...@gmail.com

unread,
Nov 7, 2017, 3:58:18 AM11/7/17
to ISO C++ Standard - Future Proposals
Hello Corentin,

It looks good. I think the main issues are addressed, and the uniformity/intuitiveness is clear.
However, I think you should address also how those concepts can be mix with other qualifiers (const, volatile, *, &) in variable declarations and function parameters.
There were no consensus on that in the previous thread (AFAIR).

Florian

Corentin

unread,
Nov 7, 2017, 4:18:32 AM11/7/17
to ISO C++ Standard - Future Proposals, floria...@gmail.com
I do address that a bit near the end of the document, maybe I should be more detailed.
the idea ( and I'm sorry for not speaking standardeese well at all )  is to say that the  "concept-name-sequence type-id" is the entity to which the qualifiers apply.
So const, volatile, *, &, etc will appear either before the concept sequence or after the type name.
Of course, both the type and the concepts can have a nested name qualifier.

const std::Constructible auto & x =
constexpr  bar::GreaterThan<42> std::size_t s =
const BadlyDesigned Foo *** const*** bar =  

Or to put it more simply, concepts appear always immediately before the type and concepts are immediately followed by a type.
I think it's the simplest, less confusing way to do it.

Was there alternative suggestions ?

floria...@gmail.com

unread,
Nov 7, 2017, 4:34:10 AM11/7/17
to ISO C++ Standard - Future Proposals
I'm also not a standard expert at all. So please take my words with a grain of salt.

I'm completely fine with this grammar. But I think it should be more explicit in the proposal.
I don't know if anybody has other suggestion.

Tom Honermann

unread,
Nov 15, 2017, 10:57:02 AM11/15/17
to std-pr...@isocpp.org, Corentin, Thomas Köppe, Jakob Riedle
In section 1.1, the template parameters list doesn't include template template parameters:
  template <template <typename> class T>
I suggest also noting that in the concept constrained case, the kind of template parameter can be any of type, non-type, or template (according to the concept's prototype template parameter; see http://eel.is/c++draft/temp.param#10)

In section 1.2, the example code binds 'r' to two different things (a template parameter, and a function parameter).  Perhaps you meant this?
  template <Regular R>
  void foo(R & r);

In section 1.2, the statement that "lambda cannot be constrained" is no longer true; P0766 [1] as adopted in Albuquerque provides for requires clauses in lambda expressions.

In section 2.1: support for 'auto' as a function parameter was rejected (for now) in Albuquerque when P0766 [1] was discussed.  This will be revisited in Jacksonville.

In section 2.4.2, I recommend making it ill-formed for *any* constraints to be specified with the initial 'auto' in a function declaration that includes a trailing return type (don't just require that the constraints match).

I like the discussion in 2.4.4; I don't think I've seen anyone suggest use of concept names in place of 'typename' for dependent name disambiguation.  I'm not yet sure if this is a good idea, but it is interesting to think about.  I presume that the named concept's prototype parameter must be a type template parameter for use in place of 'typename'.  Perhaps you could also allow a concept with a prototype template template parameter to be used in place of 'template' disambiguation.  Would you also allow a concept with a non-type prototype template parameter to be used where 'typename' and 'template' disambiguation is not required?  I'm not sure that this is useful, nor whether it is reasonably possible within the existing grammar.

In section 2.4.5, I believe it is already allowed to specify constrained parameters in template alias declarations.  I don't understand the final example; is 'Stream' effectively a concept alias (I assume 'Iterable' names a concept)?  That declaration also looks like a partial specialization of a template alias, but those don't exist.  Perhaps you intended 'template<Serializable T> using Stream = Iterable<T>'?

In section 2.4.6, there is no explanation of *why* you feel constraints would be actively harmful in the cases mentioned.  The meta-classes proposal includes the ability to associate a constraint with a class template definition that would require all instantiations to satisfy the constraints; I find that potentially useful.

In section 2.5: I think it is potentially useful to allow constraints to be specified on pointers and references just as we can with existing type qualifiers.  For example:
  auto * PointerToIntegral p = ...;  // The type of 'p' must satisfy PointerToIntegral.

In section 3, note that 'void foo(ConceptName auto a, decltype(a) b)' is *not* equivalent to the consistent resolution behavior specified in the concepts TS because the type of the argument for 'b' does not contribute to type deduction.  See P0725 [2] for some discussion of this.  Specifying consistent resolution when concept names are not required to deduce the same type requires a template header.  For example:
  template<ConceptName typename T> void foo(T a, T b);

Overall, I think the paper would benefit from more separation of motivation from what is actually proposed.  I find the discussion sometimes makes it confusing exactly what is and is not proposed.

What I would really like to see is for you, Jacob Riedle and Thomas Köppe to join forces and author a single paper that presents the motivation for these designs, the design options and tradeoffs, and a single proposal.  Ideally, the paper would be presented in a form that enabled EWG to vote on (and finally, hopefully, settle) a number of design issues:
- Whether an abbreviated function syntax must have a 'template' keyword in the declaration.
- Consistent-resolution vs independent-resolution.
- Concepts-as-qualifiers (adjectives) vs concepts-as-type-specifiers.

Tom.

[1]: http://wg21.link/p0766
[2]: http://wg21.link/p0725
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/1abe0443-19be-4aff-8627-e87d1807b901%40isocpp.org.


Corentin

unread,
Nov 15, 2017, 12:22:45 PM11/15/17
to Tom Honermann, std-pr...@isocpp.org, Thomas Köppe, Jakob Riedle
Le mer. 15 nov. 2017 à 16:57, Tom Honermann <t...@honermann.net> a écrit :
In section 1.1, the template parameters list doesn't include template template parameters:
  template <template <typename> class T>
I suggest also noting that in the concept constrained case, the kind of template parameter can be any of type, non-type, or template (according to the concept's prototype template parameter; see http://eel.is/c++draft/temp.param#10)

I indeed did not address template template parameters. I must admit I'm not 100% comfortable with them, but the idea would be to do something like  template <template <typename> [concept-name-list] [class] T>.
There are also proposal to extend what can constitute a non-type non-template template parameter.

 

In section 1.2, the example code binds 'r' to two different things (a template parameter, and a function parameter).  Perhaps you meant this?
  template <Regular R>
  void foo(R & r)

Indeed, will fix. I can't believed that survived as many proof-reading as it did. 
 
In section 1.2, the statement that "lambda cannot be constrained" is no longer true; P0766 [1] as adopted in Albuquerque provides for requires clauses in lambda expressions.

Great, I wasn't aware.


In section 2.1: support for 'auto' as a function parameter was rejected (for now) in Albuquerque when P0766 [1] was discussed.  This will be revisited in Jacksonville.

I know it was, I am still try to fight for it. To the end.
 

In section 2.4.2, I recommend making it ill-formed for *any* constraints to be specified with the initial 'auto' in a function declaration that includes a trailing return type (don't just require that the constraints match).

Your are certainly right, that's probably the most reasonable approach
 

I like the discussion in 2.4.4; I don't think I've seen anyone suggest use of concept names in place of 'typename' for dependent name disambiguation.  I'm not yet sure if this is a good idea, but it is interesting to think about.  I presume that the named concept's prototype parameter must be a type template parameter for use in place of 'typename'.  Perhaps you could also allow a concept with a prototype template template parameter to be used in place of 'template' disambiguation.  Would you also allow a concept with a non-type prototype template parameter to be used where 'typename' and 'template' disambiguation is not required?  I'm not sure that this is useful, nor whether it is reasonably possible within the existing grammar.

The idea comes P0634, and the general observation that typename is one of this thing that gets mindlessly and grudgingly added by junior developers when the compiler is unhappy. Hopefully, replacing that keyword with actual information will make people think more about what they are doing, and add information for subsequent readers. And typename was already a constraint in that it force the type of the identifier to be a type. If that make sense.

If I understand you correctly, what your propose would be like having a constrained "valuename" ? The compilers could probably deal with it, however would it add readability ?
You probably don't want to loose the "this is a type" information for the human reader.

 

In section 2.4.5, I believe it is already allowed to specify constrained parameters in template alias declarations.  I don't understand the final example; is 'Stream' effectively a concept alias (I assume 'Iterable' names a concept)?  That declaration also looks like a partial specialization of a template alias, but those don't exist.  Perhaps you intended 'template<Serializable T> using Stream = Iterable<T>'?


The idea that actually was suggested by several people independently,  was to allow a an alias of a template type to be more constrained than the original type. in this poorly made and explained example, Stream is an alias on the Iterate template type, however it puts additional requirements on the type ( or non type ) of its template parameter. In insight, it may be worth of its own paper. 
I wouldn't say it's a specialization, since it does not affect the declaration.

 
In section 2.4.6, there is no explanation of *why* you feel constraints would be actively harmful in the cases mentioned.  The meta-classes proposal includes the ability to associate a constraint with a class template definition that would require all instantiations to satisfy the constraints; I find that potentially useful.

Because it couples concepts and types, and then it starts to look more like interfaces or inheritance. But maybe you are right that people may want to advertise that a type respect a set of constraints.
I feel like it could have unwanted consequences, in the way people use and see concepts?

You certainly made me realize I have to think about that more.



 
In section 2.5: I think it is potentially useful to allow constraints to be specified on pointers and references just as we can with existing type qualifiers.  For example:
  auto * PointerToIntegral p = ...;  // The type of 'p' must satisfy PointerToIntegral.

Maybe ? I feel like this would have very limited use.   We have syntax to describe qualifiers, I don't think concepts add a lot information there.  
But why not, as long as compilers writers have no reasons to be opposed to it.

 

In section 3, note that 'void foo(ConceptName auto a, decltype(a) b)' is *not* equivalent to the consistent resolution behavior specified in the concepts TS because the type of the argument for 'b' does not contribute to type deduction.  See P0725 [2] for some discussion of this.  Specifying consistent resolution when concept names are not required to deduce the same type requires a template header.  For example:
  template<ConceptName typename T> void foo(T a, T b);

That's true. And I'm not arguing against that syntax. I'm arguing that in functions template with auto parameter, each constraint should only apply to the type immediately following.
Note that this does not break your example, ConceptName only appears once in front of T. 

 
Overall, I think the paper would benefit from more separation of motivation from what is actually proposed.  I find the discussion sometimes makes it confusing exactly what is and is not proposed.

Hum. I will try to work on on that. I was suggested to offer a summary of the proposed syntaxes and your confusion is probably a good tell that it is indeed necessary.
 

What I would really like to see is for you, Jacob Riedle and Thomas Köppe to join forces and author a single paper that presents the motivation for these designs, the design options and tradeoffs, and a single proposal.  Ideally, the paper would be presented in a form that enabled EWG to vote on (and finally, hopefully, settle) a number of design issues:
- Whether an abbreviated function syntax must have a 'template' keyword in the declaration.
- Consistent-resolution vs independent-resolution.
- Concepts-as-qualifiers (adjectives) vs concepts-as-type-specifiers.

I did try to contact Jacob Riedle, we will see what comes from that.
You are right that we would benefit from more discussion, though we definitively have different take on this issue, even though there are lot of similarities.


I hope I answered your questions.
Your feedback was really valuable and appreciated. 

Corentin

Tom Honermann

unread,
Nov 15, 2017, 10:59:19 PM11/15/17
to Corentin, std-pr...@isocpp.org, Thomas Köppe, Jakob Riedle
On 11/15/2017 12:22 PM, Corentin wrote:
Le mer. 15 nov. 2017 à 16:57, Tom Honermann <t...@honermann.net> a écrit :
 

I like the discussion in 2.4.4; I don't think I've seen anyone suggest use of concept names in place of 'typename' for dependent name disambiguation.  I'm not yet sure if this is a good idea, but it is interesting to think about.  I presume that the named concept's prototype parameter must be a type template parameter for use in place of 'typename'.  Perhaps you could also allow a concept with a prototype template template parameter to be used in place of 'template' disambiguation.  Would you also allow a concept with a non-type prototype template parameter to be used where 'typename' and 'template' disambiguation is not required?  I'm not sure that this is useful, nor whether it is reasonably possible within the existing grammar.

The idea comes P0634, and the general observation that typename is one of this thing that gets mindlessly and grudgingly added by junior developers when the compiler is unhappy. Hopefully, replacing that keyword with actual information will make people think more about what they are doing, and add information for subsequent readers. And typename was already a constraint in that it force the type of the identifier to be a type. If that make sense.

If I understand you correctly, what your propose would be like having a constrained "valuename" ? The compilers could probably deal with it, however would it add readability ?
You probably don't want to loose the "this is a type" information for the human reader.

I didn't intent to propose it, nor suggest that it might be useful, but maybe worth thinking about.  Maybe.  I can't think of a reasonably motivating example.  In each example I've thought of, I find myself thinking the constraint belongs in the requires clause of the template.  But that seems likely to be true for cases where you would specify a concept name in place of 'typename' or 'template' as well, so maybe there just isn't much motivation for this at all.


 

In section 2.4.5, I believe it is already allowed to specify constrained parameters in template alias declarations.  I don't understand the final example; is 'Stream' effectively a concept alias (I assume 'Iterable' names a concept)?  That declaration also looks like a partial specialization of a template alias, but those don't exist.  Perhaps you intended 'template<Serializable T> using Stream = Iterable<T>'?


The idea that actually was suggested by several people independently,  was to allow a an alias of a template type to be more constrained than the original type. in this poorly made and explained example, Stream is an alias on the Iterate template type, however it puts additional requirements on the type ( or non type ) of its template parameter. In insight, it may be worth of its own paper. 
I wouldn't say it's a specialization, since it does not affect the declaration.

The semantics are still unclear to me; I think this section could use some more explanation as well as an example.  How do I use this 'Stream' alias?  Is 'Stream<MyType> X;' a valid declaration that is only well-formed if 'MyType' satisfies both 'Iterable' and 'Serializable'?  Can I use 'Stream' as a constrained-parameter as in 'template<Stream T> class X;'?  If so, this seems to be introducing a concept alias as opposed to a type alias; which seems unnecessary since 'template<typename T> concept Stream = requires Serializable<T> && Iterable<T>' achieves the same result.

Assuming I'm following correctly, I think a different syntax is necessary since 'template<Serializable T> using Stream<T> = ...' is the syntax that would be expected for a partial specialization of an alias template (if such things existed).  This is why I asked if the '<T>' at the end of 'Stream' is really intended.


 
In section 2.5: I think it is potentially useful to allow constraints to be specified on pointers and references just as we can with existing type qualifiers.  For example:
  auto * PointerToIntegral p = ...;  // The type of 'p' must satisfy PointerToIntegral.

Maybe ? I feel like this would have very limited use.   We have syntax to describe qualifiers, I don't think concepts add a lot information there.  
But why not, as long as compilers writers have no reasons to be opposed to it.

I agree the uses would be relatively rare, but I like that this follows the existing rules for type qualifiers.  It does complicate interaction with 'const' and 'volatile', but perhaps no more so than is necessary anyway.  I think your section 2.5 could provide an example like the following:

  const Fooable auto foo = {};
  Fooable const auto foo = {};
  const auto foo Fooable = {};  // ill-formed because the constraint follows the name?
  Fooable auto foo const = {};
  auto foo const Fooable = {};  // ill-formed because the constraint follows the name?
  auto foo Fooable const = {};  // ill-formed because the constraint follows the name?

Are all of these semantically equivalent?  Are some ill-formed as indicated?



 

In section 3, note that 'void foo(ConceptName auto a, decltype(a) b)' is *not* equivalent to the consistent resolution behavior specified in the concepts TS because the type of the argument for 'b' does not contribute to type deduction.  See P0725 [2] for some discussion of this.  Specifying consistent resolution when concept names are not required to deduce the same type requires a template header.  For example:
  template<ConceptName typename T> void foo(T a, T b);

That's true. And I'm not arguing against that syntax. I'm arguing that in functions template with auto parameter, each constraint should only apply to the type immediately following.
Note that this does not break your example, ConceptName only appears once in front of T.

Right, you are arguing for independent resolution; which I'm in favor of as well.  The point I was trying to make is that 'void foo(ConceptName auto a, decltype(a) b)' is probably *not* the desired semantics in most cases (though it is in some cases).

Tom.

mihailn...@gmail.com

unread,
Jan 6, 2018, 8:14:52 AM1/6/18
to ISO C++ Standard - Future Proposals
Hello, I wanted to share my thoughts on the broad topic of "Concepts as Adjectives". 

I believe they are not as innocent as presented.


First, a new category of user provided entities is silently introduced - type decorators. This is bigger a change then implied.

Up until this point all decorations were either keywords or attributes, and now we have something which looks like a type (or even a template!), used as a qualifier to some other (real) type or auto. 
Sooner or later people will start asking, why I just can write only the decorator and the type be deduced, which the paper actually allow. In that case question arises do why do we need this new category in the first place.


Second. Consider const Point arg. const is not about the type, it's about the variable. The fact that C++ handles const as a different type of Point is an implementation detail.
However "type decorations" will be all about the type, in that one line we will have some decorations which semantically are about the variable and others about the type of it! This is bound to lead to confusion.

Also. const int a; will always bind to an int b, but a MyClass c, might not bind to MyConstraign Class d; Yet both are spelled out with the same syntax, both are "a case where one is just more constrained then the other", but in practice mean different things.  


Third. Consider A auto a = ...; B int b = ...; From the C++ rules so far we might consider both of these are semantically the same, just in the second case the auto is spelled out. In fact however these two, are radically different - the first is constrained type, the other is constrained value. This can be confusing no matter how we look at it - if we look from variable declaration PoV we expect both to be types (but they are not) if we look at template argument declaration PoV (<auto I>, <int I>) we expect both to be a value, which is also not true!

The confusion with out continues with the fact A auto means one thing in code (as return value, variable or argument) and different as template param.
f(A auto a){}; A auto a; A auto f() {} are (radically) different then template<B auto I>.
Yes, one can argue this is already the case, but this does not help either, not to mention auto as function params is not yet in.


Forth. Decorations are, by there nature, an ad hoc tool - to have something existing and to further specify it in the place of use. Constrained types are not like that, ad hoc constrain creation is unlikely. Swappable Sortable Writable foo; in the real world will likely be just FooConcept foo; semantically. It is very unlikely one will need to mix and match constrains on the go and pay the syntax, maintenance, visual noise tax.


I believe we should streamline Concepts as much as possible actually removing features and "optional syntaxes" before adding new ones. By streamline I don't mean a "terse syntax", just a sane one, be it a verbose one (at first).
And BTW a non-sane syntax is to have both function and variable way to declare a concept, to have that stupid bool and so on, in other words most (but not all) of what is written in Concepts TS revisited

Thanks
MN


Jakob Riedle

unread,
Jan 8, 2018, 10:42:50 AM1/8/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com


Am Samstag, 6. Januar 2018 14:14:52 UTC+1 schrieb mihailn...@gmail.com:
Hello, I wanted to share my thoughts on the broad topic of "Concepts as Adjectives". 

I believe they are not as innocent as presented.
 
Yes, in my opinion, Concepts as adjectives/qualifiers are very very powerful and in no way a diffident solution.
 


First, a new category of user provided entities is silently introduced - type decorators. This is bigger a change then implied.
 
When I first wrote "Concepts are Adjectives, Not Nouns", I very well perceived them to be ground breaking.
 


Up until this point all decorations were either keywords or attributes, and now we have something which looks like a type (or even a template!), used as a qualifier to some other (real) type or auto. 
 
I agree, that it introduces identifiers (in constrast to keyword) as qualifiers, which is unprecedented by now.
However, this identifier is hardly visually ambiguated with a type name, because it ultimately is followed by a concrete type (least common case), "auto" or "typename" (most common cases).
In all situations, the noun is (in 9 of 10 cases) the last identifier, which sets it visually apart.
In the latter two cases, the noun is a keyword and therefore additionally visually set apart.
 


Sooner or later people will start asking, why I just can write only the decorator and the type be deduced, which the paper actually allow. In that case question arises do why do we need this new category in the first place.
 
Because sometimes, we want to declare a constrained type (Sortable typename S) and sometimes we want to declare a variable of constrained type (Sortable auto s).
If you ask me, I am against making the noun optional in any case. Actually for the same reason that C++ discourages "const" to implicitly stand for "const int" (as in C).



Second. Consider const Point arg. const is not about the type, it's about the variable. The fact that C++ handles const as a different type of Point is an implementation detail.
However "type decorations" will be all about the type, in that one line we will have some decorations which semantically are about the variable and others about the type of it! This is bound to lead to confusion.
 
Wait, do you think going from "template<typename T>" (without concepts) to "template<Sortable T>" is less confusing?
Suddenly, we don't know whether T is a type after all?!
 


Also. const int a; will always bind to an int b, but a MyClass c, might not bind to MyConstraign MyClass d; Yet both are spelled out with the same syntax, both are "a case where one is just more constrained then the other", but in practice mean different things.
 
Firstly, this argument is not working for me, since const int& will not bind to a int&. Secondly:
If (which I assume) MyClass  is a concrete type,
  • either MyConstrain is a Value-Constraint
    (in which case we face same "issue" (IMO, that's a feature), that Concept TS faces),
  • or MyConstrain is a Type-Constraint
    (in which case "MyClass" does very well bind to "MyConstraint MyClass", given MyClass after all meets MyConstraint and doesn't generate a compiler error).


Third. Consider A auto a = ...; B int b = ...; From the C++ rules so far we might consider both of these are semantically the same, just in the second case the auto is spelled out. In fact however these two, are radically different - the first is constrained type, the other is constrained value. This can be confusing no matter how we look at it - if we look from variable declaration PoV we expect both to be types (but they are not) if we look at template argument declaration PoV (<auto I>, <int I>) we expect both to be a value, which is also not true!
 
I understand your concerns. However, this is not how I and Corentin propose Concepts.
Whether a Concept constraints the type or the value of a variable depends on the concept, not on whether we "spell out the auto".
"Even" in the phrases "Even int i" and "Even auto i", will always constrain the values of i.
In the latter phrase however, "auto" is allowed to deduce to any type (including int),
for which the property "Even" is decidable (depending on the definition, probably for number-like types).
 


The confusion with out continues with the fact A auto means one thing in code (as return value, variable or argument) and different as template param.
f(A auto a){}; A auto a; A auto f() {} are (radically) different then template<B auto I>.
Yes, one can argue this is already the case, but this does not help either, not to mention auto as function params is not yet in.

I AM in favor of "auto" having the same meaning (regarding your concerns) across its usages.
This is the way I proposed it and I believe Corentin would too.
 


Forth. Decorations are, by there nature, an ad hoc tool - to have something existing and to further specify it in the place of use. Constrained types are not like that, ad hoc constrain creation is unlikely. Swappable Sortable Writable foo; in the real world will likely be just FooConcept foo; semantically. It is very unlikely one will need to mix and match constrains on the go and pay the syntax, maintenance, visual noise tax.

For the sake of argument, it's irrelevant how likely this feature is used.
But if at all unlikely, this is a feature, not a bug. Nobody forces you to chain concepts this way.
Apart from that, I hardly believe, you will write a named concept for each and every such Concept-based function.
Why would you do so, if you only needed to chain the Concepts once for the whole function?
 


I believe we should streamline Concepts as much as possible actually removing features and "optional syntaxes" before adding new ones. 

I agree with you here, although I don't find "Concepts TS revisited" to make things consistent enough.



When ansering to your concerns, I hope to have understood you correctly for the most part.
If not, please clarify those mistakes.

Yours,
Jakob

Corentin

unread,
Jan 8, 2018, 12:03:28 PM1/8/18
to std-pr...@isocpp.org, mihailn...@gmail.com, Jakob Riedle
Please find my answers bellow, they merely complement the great answers given by jakob.
I hope they help, otherwise let me know !

Corentin

Le lun. 8 janv. 2018 à 16:42, Jakob Riedle <jakob....@gmail.com> a écrit :


Am Samstag, 6. Januar 2018 14:14:52 UTC+1 schrieb mihailn...@gmail.com:
Hello, I wanted to share my thoughts on the broad topic of "Concepts as Adjectives". 

I believe they are not as innocent as presented.
 
Yes, in my opinion, Concepts as adjectives/qualifiers are very very powerful and in no way a diffident solution.

First, a new category of user provided entities is silently introduced - type decorators. This is bigger a change then implied.
 
When I first wrote "Concepts are Adjectives, Not Nouns", I very well perceived them to be ground breaking.

Yet I'm convinced they make *using* the language easier and are it's not a complex feature either. I think it's a natural extension of the current Concepts TS.  
 

Up until this point all decorations were either keywords or attributes, and now we have something which looks like a type (or even a template!), used as a qualifier to some other (real) type or auto. 
 
I agree, that it introduces identifiers (in constrast to keyword) as qualifiers, which is unprecedented by now.
However, this identifier is hardly visually ambiguated with a type name, because it ultimately is followed by a concrete type (least common case), "auto" or "typename" (most common cases).
In all situations, the noun is (in 9 of 10 cases) the last identifier, which sets it visually apart.
In the latter two cases, the noun is a keyword and therefore additionally visually set apart.

Note that constraints do have an impact on overload resolution, but they do not "qualify" the name in that a Sortable Foo is still a Foo.
The list of concepts and the name should be read a single entity to which you apply any other qualifier;
(cv) type (ptr-declarator), except now the type may be preceded by a list of concepts names. So I don't think there is any ambiguity.  
 

Sooner or later people will start asking, why I just can write only the decorator and the type be deduced, which the paper actually allow. In that case question arises do why do we need this new category in the first place.
 
Because sometimes, we want to declare a constrained type (Sortable typename S) and sometimes we want to declare a variable of constrained type (Sortable auto s).
If you ask me, I am against making the noun optional in any case. Actually for the same reason that C++ discourages "const" to implicitly stand for "const int" (as in C).

While Jakob and I have some disagreements regarding whether auto could be optional, in any case, I don't think there could be any confusion.
And why would you use a concept name rather than auto? Because in most cases auto is too vague. especially as a type parameter of a function.
Our hope is that a wide use of concepts will lead to cleaner, more comprehensive generic apis. 


Second. Consider const Point arg. const is not about the type, it's about the variable. The fact that C++ handles const as a different type of Point is an implementation detail.
However "type decorations" will be all about the type, in that one line we will have some decorations which semantically are about the variable and others about the type of it! This is bound to lead to confusion.
 
Wait, do you think going from "template<typename T>" (without concepts) to "template<Sortable T>" is less confusing?
Suddenly, we don't know whether T is a type after all?!
 


Also. const int a; will always bind to an int b, but a MyClass c, might not bind to MyConstraign MyClass d; Yet both are spelled out with the same syntax, both are "a case where one is just more constrained then the other", but in practice mean different things.
 
Firstly, this argument is not working for me, since const int& will not bind to a int&. Secondly:
If (which I assume) MyClass  is a concrete type,
  • either MyConstrain is a Value-Constraint
    (in which case we face same "issue" (IMO, that's a feature), that Concept TS faces),
  • or MyConstrain is a Type-Constraint
    (in which case "MyClass" does very well bind to "MyConstraint MyClass", given MyClass after all meets MyConstraint and doesn't generate a compiler error)
Exactly, constraints are assertions on types, not transformations.
 

Third. Consider A auto a = ...; B int b = ...; From the C++ rules so far we might consider both of these are semantically the same, just in the second case the auto is spelled out. In fact however these two, are radically different - the first is constrained type, the other is constrained value. This can be confusing no matter how we look at it - if we look from variable declaration PoV we expect both to be types (but they are not) if we look at template argument declaration PoV (<auto I>, <int I>) we expect both to be a value, which is also not true!
 
I understand your concerns. However, this is not how I and Corentin propose Concepts.
Whether a Concept constraints the type or the value of a variable depends on the concept, not on whether we "spell out the auto".
"Even" in the phrases "Even int i" and "Even auto i", will always constrain the values of i.
In the latter phrase however, "auto" is allowed to deduce to any type (including int),
for which the property "Even" is decidable (depending on the definition, probably for number-like types).
 


The confusion with out continues with the fact A auto means one thing in code (as return value, variable or argument) and different as template param.
f(A auto a){}; A auto a; A auto f() {} are (radically) different then template<B auto I>.
Yes, one can argue this is already the case, but this does not help either, not to mention auto as function params is not yet in.

I AM in favor of "auto" having the same meaning (regarding your concerns) across its usages.
This is the way I proposed it and I believe Corentin would too.

In all the above cases the constrained are applied to a value.
What you may find confusing is just the nature of non type non template template parameters. Which is something I certainly can relate too.  
 

Forth. Decorations are, by there nature, an ad hoc tool - to have something existing and to further specify it in the place of use. Constrained types are not like that, ad hoc constrain creation is unlikely. Swappable Sortable Writable foo; in the real world will likely be just FooConcept foo; semantically. It is very unlikely one will need to mix and match constrains on the go and pay the syntax, maintenance, visual noise tax.

For the sake of argument, it's irrelevant how likely this feature is used.
But if at all unlikely, this is a feature, not a bug. Nobody forces you to chain concepts this way.
Apart from that, I hardly believe, you will write a named concept for each and every such Concept-based function.
Why would you do so, if you only needed to chain the Concepts once for the whole function?

Additionally, You may want to manipulate multiple properties of an object within the same function. You are not implicitly creating a new concept, you are relying on several.
There is a subtle, yet important distinction.
Using the same list of concepts name over and over would be a good sign that you indeed, need a new concept name.

 


I believe we should streamline Concepts as much as possible actually removing features and "optional syntaxes" before adding new ones. 

I agree with you here, although I don't find "Concepts TS revisited" to make things consistent enough.



When ansering to your concerns, I hope to have understood you correctly for the most part.
If not, please clarify those mistakes.

Yours,
Jakob

--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

mihailn...@gmail.com

unread,
Jan 8, 2018, 12:22:13 PM1/8/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
My example was not very clear, but issue is still there, I believe - copyConstructable T will not bind to any T, however const T does not impose restrictions to the incoming T.
const is not a required clause, copyConstructable is, yet both are used exactly the same way.  

This mixing of qualifiers could be confusing - sometimes it is about the type, sometimes it is about the value, sometime it is about the variable.




Third. Consider A auto a = ...; B int b = ...; From the C++ rules so far we might consider both of these are semantically the same, just in the second case the auto is spelled out. In fact however these two, are radically different - the first is constrained type, the other is constrained value. This can be confusing no matter how we look at it - if we look from variable declaration PoV we expect both to be types (but they are not) if we look at template argument declaration PoV (<auto I>, <int I>) we expect both to be a value, which is also not true!
 
I understand your concerns. However, this is not how I and Corentin propose Concepts.
Whether a Concept constraints the type or the value of a variable depends on the concept, not on whether we "spell out the auto".
"Even" in the phrases "Even int i" and "Even auto i", will always constrain the values of i.
In the latter phrase however, "auto" is allowed to deduce to any type (including int),
for which the property "Even" is decidable (depending on the definition, probably for number-like types).

Then we still can't tell if the concept is about the type or the value. Wasn't it one of the original goals to have that distinction?
In any case, I understand variable declaration is a separate issue! 

Corentin

unread,
Jan 8, 2018, 12:39:21 PM1/8/18
to std-pr...@isocpp.org, mihailn...@gmail.com
Constraints are associated to a type most of the type.

Values as in non type non template template parameter are odd enough, and rare enough to begin with that I don't really see them as an issue.

Note that a concept can never apply to both a value and a type. `Even` is a value concept.

In all other cases, the constrains are associated to a type. Either a single value of that type (auto) , or a type as a whole (typename).


--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.

Jakob Riedle

unread,
Jan 8, 2018, 1:51:52 PM1/8/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com


Am Montag, 8. Januar 2018 18:22:13 UTC+1 schrieb mihailn...@gmail.com:
My example was not very clear, but issue is still there, I believe - copyConstructable T will not bind to any T, however const T does not impose restrictions to the incoming T.
const is not a required clause, copyConstructable is, yet both are used exactly the same way.  

This mixing of qualifiers could be confusing - sometimes it is about the type, sometimes it is about the value, sometime it is about the variable.

Well, your example probably was intended to go the other way around, right?
I see your point. As I think about it: const relaxes the constraints implicitly put on a variable.
While a non-const-qualified Variable is expected to be modifieable (that's an implicit constraint),
a const-qualified variable does not require this constraint to be met.

Now comes the point: You can always cast from something more constrained to something less constrained (see contract-based-programming).
The confuse comes into play because this topic has never been touched by anyone before (TMK)  :P

Does that make sense?


Similarly, "volatile" doesn't require the variable to be appropriately loadable from cache.
While a normal variable is implicitly expected to not change from outside the programs environment, a volatile variable is allowed to do that.
Again: relaxed Constraints.

That's really interesting for me right now!
Funnily, the constraints on a normal variable are not "zero" and you can lower constraints below that!

Thank you for this enlightening comment of yours!
 

Third. Consider A auto a = ...; B int b = ...; From the C++ rules so far we might consider both of these are semantically the same, just in the second case the auto is spelled out. In fact however these two, are radically different - the first is constrained type, the other is constrained value. This can be confusing no matter how we look at it - if we look from variable declaration PoV we expect both to be types (but they are not) if we look at template argument declaration PoV (<auto I>, <int I>) we expect both to be a value, which is also not true!
 
I understand your concerns. However, this is not how I and Corentin propose Concepts.
Whether a Concept constraints the type or the value of a variable depends on the concept, not on whether we "spell out the auto".
"Even" in the phrases "Even int i" and "Even auto i", will always constrain the values of i.
In the latter phrase however, "auto" is allowed to deduce to any type (including int),
for which the property "Even" is decidable (depending on the definition, probably for number-like types).

Then we still can't tell if the concept is about the type or the value. Wasn't it one of the original goals to have that distinction?
In any case, I understand variable declaration is a separate issue! 

No, the original goal was to know, whether an identifier constrained by a concept refers a type or value.
Obviously, Value-Concepts can only constrain values, but Type-Concepts can both constrain "auto" variables and "typename" variables.
However, Value-Concepts can - depending on their definition - implicitly constrain the type of an "auto" Variable, because a given property is only decidable for a given set of datatypes.

mihailn...@gmail.com

unread,
Jan 8, 2018, 2:49:35 PM1/8/18
to ISO C++ Standard - Future Proposals, mihailn...@gmail.com
The fact stays, one can't tell if a Concept is a value or type one, right?  The compiler knows, of course, but the user must look up the definition (or judge by the name), not unlike in the original problem.

And I am still concerned, we now have not just a new category (user-provided type decorator), but we add two new decorator categories. Where before we just for the variable, now we have two more (for type and value), all spelled right next to each other.


Now, that I think about it I mainly have a problems with variable declarations, as template params I am not that worried. But, then, all papers revolve primarily around variable declarations, to solve the terse syntax argument problem.


As I said initially, I am more worried right now, Concepts might be a bit messy, from what I saw, which is unfortunate, considering the more or less the clean slate. And the messy part for me is the definition, not the usage.
 
Reply all
Reply to author
Forward
0 new messages