Closed RPeschke closed 1 year ago
Your intention is aligned with some of the goals of this project:
The issue I see is that I am unaware of any coding guidelines that recommend something like that (I believe this is possible already with the use of the macros).
Please note that cpp2 introduces less noise already by giving a chance to developer to express intention and not to focus on selecting the proper set of keywords:
I have another thought about that topic. We are already in a situation where the community generated a lot of keywords that need to be simplified by selecting reasonable defaults and simpler ways to express the intention.
The issue I see is that I am unaware of any coding guidelines that recommend something like that (I believe this is possible already with the use of the macros).
Command–query separation is a well known concept. I use it when appropriate but I do not follow it mindlessly.
Parts of it can already be implemented with macros yes but overall I think we all want to move away from macros. Also since some of the function qualifier are before and some are after the function name it cannot be done with just one simple macro.
As I mentioned one quick fix would be to allow all function qualifier to be set before the function name and then use macros.
I am not convinced that making the default [[nodiscard]] will be correct in 90% of the cases (it will be correct in most cases). There are good reasons for having a return value ignored. take vector::insert for example most people will not use the return value ever but it is still good to have the option. By defining insert as command it might be easier to see that the return value is optional and the main purpose of the function is to modify the container.
Also I am well aware of language that take the "Command–query separation" to far and allow no overlap between the two functions which creates more problems then it solves. But having the possibility to use the pattern when appropriate might be good.
I was not clear about the guidelines I referred to. I am aware of the Command query separation rule. I am not aware of C++ rule that proposes replacing multiple keywords with the custom one e.g. using macros.
Thanks @RPeschke!
The short answer is that Yes, I plan this for classes and hopefully also for functions via static reflection + generative programming.
For classes, see all the talks and papers at README > 2017: Reflection, generation, and metaclasses, and for code examples primarily look at the many examples in the body of paper P0707. Some prototypes have been implemented in a Clang fork using an extension of today's C++ syntax. The idea is that instead of writing every class's boilerplate by hand, you can write something like this (in possible upcoming Cpp2 syntax, not yet finalized or implemented):
// Declare the intent that this type be a "value": see "value" in P0707 for details
// (e.g., is copyable and movable, and if those aren't provided they're automatically generated)
mystring: value type = { /*...*/ }
// Declare the intent that this type be an "interface": see "interface" in P0707 for details
// (e.g., must not have any data members, and all functions are public pure virtual by default)
IShape: interface type = { /*...*/ }
Note that the idea is that value
and interface
not be magical language features hardwired into the compiler, but be C++/Cpp2 code that's executed at compile time that can do reflection and generation. That means that (once your compiler has reflection and generation) you don't have to wait for your compiler implementer to implement each of them since they're just code, and also that you can write your own custom ones as just compile-time code.
I hope to extend the same idea for functions, down the road. That is, I would like to be able to do something like:
// Declare the intent that this is a "pure" function
// (e.g., its statements and expressions must use only parameters/local values,
// and doing otherwise would give a compile-time error)
add: pure (a:int, b:int) = { /* can only use a and b here, not allowed to read globals for example */ }
Again, the idea is that pure
isn't a magical thing specified in the language and hardwired into the compiler, but that it's code executed at compile time that does reflection on the body.
Another example where this reflection+generation approach could be useful for functions is to implement automatic differentiation (aka "autodiff").
I think this would support the kinds of examples you have above.
But this is down the road... for now my medium-term goal is to implement classes, then reflection and generation for classes. Once that's stable we can see about functions sometime later.
I see in c++ that there is an issue that we want to be very precise about all our function decorators (constness/Noexept/nodiscard etc) but these can lead to very unreadable code (see Standard Library). I think many people especially beginners give up on trying to get all these specifier correctly because they are just to many and they are not "telling a story".
let me give an example:
As we all know "empty" could be misunderstood to be a verb in the sense of "make empty". But also it is not correctly implemented. It could be "no discard", "no except" and maybe a couple of other things too.
so let me straw man the perfect implementation a little bit over here:
The signal( function name) to noise (function qualifier) level is very low there is a lot of intimidating noise and only five letters of signal.
What we could do is, let the user define their own function keywords. For example if the user would be able to define a "query" keyword we could write the following:
This implementation would only require a minor change such as having all "function qualifier" on one side of the function. In the long run It would be good to be able to absorb all the "qualifiers" in the function keywords. Especially requirement statements.
In the final syntax one would be able to write:
I understand that in the short-term this would lead to an explosion of new keywords that might be very confusing but I think in the long run the community will settle to only a few.
Again I can't quote any book where it is discussed to be a major issue but just from my personal experience teaching c++ I see that people are very often intimidated by these long function qualifiers.
Maybe my best point of reference is that python basically has this features implemented in terms of function decorators.
simplicity: With this changes I mainly target simplicity. Not having to repeat so many function qualifier all the time might be worth while. I am not sure that it will eliminate literature but it will give a keyword (in the language) to things we teach already. We are already teaching the query pattern etc.. Lets just give it a keyword in the language. I think, being able to name things in the language instead of relying on documentation or comments is worth while.
The quantifiable improvement would be a reduction of keywords needed in order to create a function. (improved signal to noise).
DISCLAIMERS TO SET EXPECTATIONS: I'm generally against language feature requests/changes unless they can be shown to improve simplicity, safety, or toolability in a quantifiable way. So:
:
for every declaration and only for declarations.Will your feature suggestion eliminate X% of security vulnerabilities of a given kind in current C++ code? If yes, please be specific about the classes of bugs that would go away, with an example or two (especially a link to a real CVE or two).
Will your feature suggestion eliminate X% of current C++ guidance literature?" If yes, please be specific about what things we would no longer need to teach/learn or that would be simplified and how, with an example or two (especially a link to a real "Effective C++" or "C++ Core Guidelines" guideline or two). For ideas, you can refer to my CppCon 2020 talk starting at 10:31 where I summarize a categorized breakdown based on over 600 C++ guidance literature rules I cataloged and analyzed.
Describe alternatives you've considered. There's nearly always more than one way to improve something. What other options did you consider? Why is the one you're suggesting better than those?