C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Named auto

From: Keenan Horrigan <friedkeenan_at_[hidden]>
Date: Thu, 29 Sep 2022 16:57:27 +0000
> By pattern matching, I mean deducing `T` from `std::vector<T>`, or `U` and `N` from `std::array<U, N>`, or `R` and `A` from `R(*)(A...)`, etc.

Ah I see, yes the 'T{auto}' syntax doesn't do that, and I think that's okay, because if it were to do that it would lose how terse it is.

> But you can't use explicit function template syntax to initialize a local variable in block scope.

Yes, to be clear I'm not advocating against your suggestions, I think they are decent and that some form of what you're suggesting is useful and should be added to the language. I'm just advocating for some form of terse syntax to name what 'auto' would otherwise name, for the primary purpose of function parameters. I'm not in any way trying to say that such a terse syntax would solve the entire scope of the problem, because it doesn't, but that doesn't mean that it shouldn't be added.

> That is, deducing the typename `char_t` into scope but without it being a template parameter per se.

Ahh, that makes sense.
------- Original Message -------
On Thursday, September 29th, 2022 at 11:32 AM, Edward Catmur <ecatmur_at_[hidden]> wrote:

> On Thu, 29 Sept 2022 at 15:56, Keenan Horrigan <friedkeenan_at_[hidden]> wrote:
>
>>> The issues I have with that syntax are:
>>> * looks like it would be difficult/ambiguous to parse/lex (by compilers, editors, etc.)
>>> * excessively terse - nowhere else do we introduce a type name into scope without a keyword preceding it
>>> * can't pattern match, deduce non-types or more than one type
>>>
>>> For that last point, it means that that syntax would not be a complete replacement for explicit function template syntax, nor for decltype (+ worse!) at block scope, so we'd have yet more syntax to teach. I don't think much of the claim that C++ is overly complex, but I do feel that additions should try to simplify the "modern" subset of the language, i.e. those parts that are sufficient to write new code.
>>
>> The ambiguity of syntax is a concern I had too, would require more investigation to determine if it's really an issue, though that effort should not be spent before determining if it would be a good syntax in the first place.
>>
>> I can see how one might think it's excessively terse, I think I'd disagree though. Yes, type names have never before been introduced without some sort of declarative keyword (well, besides god-given types like int), but that requirement of a keyword is part of what makes the currently available options not terse enough in my opinion.
>>
>> If I understand correctly what you mean by pattern matching, I think the 'T{auto}' syntax can do that, since 'T' would just be naming what 'auto' otherwise would take the place of, so you could do 'const T{auto} &var = 1;' and the 'T' would name 'int'.
>
> By pattern matching, I mean deducing `T` from `std::vector<T>`, or `U` and `N` from `std::array<U, N>`, or `R` and `A` from `R(*)(A...)`, etc.
>
>> You're correct on the other things though, that it can't deduce non-types or multiple types, but I'm not sure a terse syntax needs to, since anything more than just putting a name to 'auto' would require syntax that is not terse enough I feel, and so if you need more complexity you would use the explicit function template syntax, which is how the current situation with abbreviated function template syntax works.
>
> But you can't use explicit function template syntax to initialize a local variable in block scope.
>
>> Thank you for the examples by the way, I think perhaps they could be better abstracted in these instances but they do demonstrate a want for block scope named deduction (though I'm not sure the last one falls under that since it's just sorta normal use of decltype).
>
> For the last one I would hope to be able to write:
> template <auto<typename char_t, size_t N> detail_exported::fixed_string<char_t, N> Str> constexpr auto operator""_cf() {
>
> That is, deducing the typename `char_t` into scope but without it being a template parameter per se.
>
>> I do think the 'T{auto}' syntax could address all the examples you gave, it might have issues when used with just plain 'auto' and maybe that 'auto' would deduce a 'const' or something which could cause issues with 'is_same', but I'm not sure the other syntax suggestions would handle that either.
>
> That's good reason to use `const` alongside `auto` - and Victor does that, to his credit. So that should work fine.
>
> Where I expect frustration to arise is when you realize that you actually wanted to pattern match on the type of the initializer.
>
>> ------- Original Message -------
>> On Thursday, September 29th, 2022 at 6:46 AM, Edward Catmur <ecatmur_at_[hidden]> wrote:
>>
>>> On Wed, 28 Sept 2022 at 19:33, Keenan Horrigan <friedkeenan_at_[hidden]> wrote:
>>>
>>>>> Yes, it's fairly common, and getting more so as previously callback-style code (that could use lambda explicit template syntax) moves to coroutines. It's frustrating when a nicely deduced, constrained and/or pattern matched parameter becomes a coroutine local variable that now can only be matched using the meagre `auto` syntax.
>>>>
>>>> Would love to see an example just for my own curiosity, but I'll take your word for it anyhow.
>>>
>>> Here's a couple of examples in just one file:
>>>
>>> https://github.com/fmtlib/fmt/blob/0ccaed3a6c745ed2a625b8d6531111ba668e4fea/include/fmt/compile.h#L206-L207
>>> https://github.com/fmtlib/fmt/blob/0ccaed3a6c745ed2a625b8d6531111ba668e4fea/include/fmt/compile.h#L317-L318
>>> https://github.com/fmtlib/fmt/blob/0ccaed3a6c745ed2a625b8d6531111ba668e4fea/include/fmt/compile.h#L541-L542
>>> https://github.com/fmtlib/fmt/blob/0ccaed3a6c745ed2a625b8d6531111ba668e4fea/include/fmt/compile.h#L600-L601
>>>
>>>> I think your 'using { ... }' syntax would be fine for named deduction for normal variable declarations, though I wouldn't mind e.g. 'auto<class T>' either, though I think the 'using' version is better and more clear. I feel though that that would only solve one aspect of this problem, and not the aspect of deduced function parameter types. Really I just want something that would give me the type that 'auto' would otherwise name in the abbreviated function template syntax, so for me something like
>>>>
>>>> void named_auto(T{auto} &&to_be_forwarded);
>>>>
>>>> is perfectly acceptable to me. Maybe these separate aspects of this problem should be given separate solutions? That runs the risk of having more than one good way to do named deduction, at least for deduced types, but I feel that may be a fine enough situation.
>>>
>>> The issues I have with that syntax are:
>>> * looks like it would be difficult/ambiguous to parse/lex (by compilers, editors, etc.)
>>> * excessively terse - nowhere else do we introduce a type name into scope without a keyword preceding it
>>> * can't pattern match, deduce non-types or more than one type
>>>
>>> For that last point, it means that that syntax would not be a complete replacement for explicit function template syntax, nor for decltype (+ worse!) at block scope, so we'd have yet more syntax to teach. I don't think much of the claim that C++ is overly complex, but I do feel that additions should try to simplify the "modern" subset of the language, i.e. those parts that are sufficient to write new code.
>>>
>>>> ------- Original Message -------
>>>> On Wednesday, September 28th, 2022 at 12:48 PM, Edward Catmur <ecatmur_at_[hidden]> wrote:
>>>>
>>>>> On Wed, 28 Sept 2022 at 17:56, Keenan Horrigan <friedkeenan_at_[hidden]> wrote:
>>>>>
>>>>>>> We already have syntax for function template parameter named deduction; we don't need another. What we don't have is a syntax for block scope named deduction.
>>>>>>
>>>>>> Well, we also currently have ways to get the types deduced through your syntax suggestions, they're just suboptimal (using decltype, remove_cvref etc.).
>>>>>
>>>>> Pattern matching the type is very much suboptimal as well; it requires creating helper metaclasses, alias templates etc., or abusing lambdas.
>>>>>
>>>>>> Similarly, I find the current state of named function parameter deduction suboptimal. We have the normal 'template<...>' syntax, and then we have the terse syntax with auto-parameters. Right now I steer away from the terse syntax because when I deduce a type for a function parameter, I very often want to also name that deduced type, so I might as well use the 'template<...>' syntax. However, the terse syntax is more concise, and I would argue more readable were it not for the fact that it necessitates some use of decltype to name the type, and similarly may require remove_cvref etc. to be applied. I find this suboptimal, and would very much enjoy being able to use the terse syntax and some form of "named auto".
>>>>>
>>>>> There's no reason that `auto<class T> T x` couldn't be accepted in function template parameter position as well; it just feels redundant.
>>>>>
>>>>> Also there's the issue that when supplying function template argument explicitly (as f<A...>(a...)) to a terse function template it's not entirely obvious which template argument corresponds to which argument. But then again, named deduction wouldn't exactly worsen this issue.
>>>>>
>>>>>> Truthfully as well, I cannot recall a time where I've wanted to name a deduced type of a variable that wasn't a function parameter. Is this a common occurrence for others? It of course would be better to have syntax that allows for it, but I personally don't find a solution that doesn't allow named deduction with the abbreviated function template syntax appealing.
>>>>>
>>>>> Yes, it's fairly common, and getting more so as previously callback-style code (that could use lambda explicit template syntax) moves to coroutines. It's frustrating when a nicely deduced, constrained and/or pattern matched parameter becomes a coroutine local variable that now can only be matched using the meagre `auto` syntax.
>>>>>
>>>>>> ------- Original Message -------
>>>>>> On Wednesday, September 28th, 2022 at 10:39 AM, Edward Catmur <ecatmur_at_[hidden]> wrote:
>>>>>>
>>>>>>> On Wed, 28 Sept 2022 at 16:24, Keenan Horrigan <friedkeenan_at_[hidden]> wrote:
>>>>>>>
>>>>>>>> My issue with the 'using' syntax was that it seems redundant when used with function parameter type deduction (because why wouldn't you just type out the normal 'template<...>'), I don't see how your 'auto<class T> T var' syntax addresses that beyond maybe putting the deduced entities closer to their source? Not a hugely compelling reason to use it over normal template parameters in my opinion.
>>>>>>>
>>>>>>> We already have syntax for function template parameter named deduction; we don't need another. What we don't have is a syntax for block scope named deduction.
>>>>>>>
>>>>>>>> ------- Original Message -------
>>>>>>>> On Wednesday, September 28th, 2022 at 10:08 AM, Edward Catmur via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>>>>>>>
>>>>>>>>> On Wed, 28 Sept 2022 at 15:14, Jason McKesson via Std-Proposals <std-proposals_at_[hidden]> wrote:
>>>>>>>>>
>>>>>>>>>> On Wed, Sep 28, 2022 at 10:06 AM Sébastien Bini via Std-Proposals
>>>>>>>>>> <std-proposals_at_[hidden]> wrote:
>>>>>>>>>>>
>>>>>>>>>>> How about a syntax similar to that of parameters then?
>>>>>>>>>>>
>>>>>>>>>>> template <class T> T var = foo();
>>>>>>>>>>> template <class K, class V> std::pair<K const, V>& kv = *map.begin(); // or [k, v]?
>>>>>>>>>>> template <std::copyable T, auto I> std::array<T, I> arr = getArr();
>>>>>>>>>>> template <std::size_t... I> [[maybe_unused]] std::index_sequence<I...> seq = std::index_sequence_for<Args...>();
>>>>>>>>>>>
>>>>>>>>>>> Then we don't need to reinvent the syntax for parameters.
>>>>>>>>>
>>>>>>>>> That syntax is already in use for variable templates. Maybe named deduction is at block scope and variable templates are at namespace scope, but that kind of overloading of meaning is pretty horrible - `static` is bad enough.
>>>>>>>>>
>>>>>>>>>> Let's re-examine the problem that people seem to want to solve.
>>>>>>>>>>
>>>>>>>>>> My understanding was that the principal problem was that you want to
>>>>>>>>>> gain access to the actual typename that the placeholder deduction
>>>>>>>>>> syntax yields, and sticking a `using name = decltype(var_name);` is
>>>>>>>>>> both wordy and error-prone (I deliberately did that wrong, for
>>>>>>>>>> example).
>>>>>>>>>>
>>>>>>>>>> The main reason people deduce types is because they don't want to have
>>>>>>>>>> to type out the full name of the type
>>>>>>>>>
>>>>>>>>> or because the type is unnameable, or because it's dependent, or because the code you're calling could change its return type and so typing it out is a hostage to fortune.
>>>>>>>>>
>>>>>>>>>> (or because it's already
>>>>>>>>>> mentioned in the initializing expression).
>>>>>>>>>
>>>>>>>>> If you're using AAA with types on the RHS then you don't need to deduce them again, so you wouldn't need named deduction.
>>>>>>>>>
>>>>>>>>>> So it seems to me that
>>>>>>>>>> injecting a bunch more words into such a declaration is...
>>>>>>>>>> counter-productive.
>>>>>>>>>>
>>>>>>>>>> Yes, any of these declarations might be useful. But... are they really
>>>>>>>>>> *that* useful? So useful that we want to type out (and read) all of
>>>>>>>>>> that?
>>>>>>>>>
>>>>>>>>> We are proposing that a single statement introduce not just potentially multiple entity names, but also potentially multiple type aliases (also, maybe, packs, templates, concepts). This is a novel challenge for compilers and other tools, and it needs *some* introducing syntax to shunt the parser down the new code paths; lexing is difficult enough as it is.
>>>>>>>>>
>>>>>>>>> If `using` is too much typing, how about the introducer being `auto <`? It seems that the lexer should be able to detect that pretty much immediately.
>>>>>>>>>
>>>>>>>>> auto<class T> T var = foo();
>>>>>>>>> // etc.

Received on 2022-09-29 16:57:33