C++ Logo

std-proposals

Advanced search

Re: Proposal: template function solution to Pythonic optional function arguments

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Mon, 20 Jul 2020 15:10:52 -0400
On Mon, Jul 20, 2020 at 12:43 PM Richard Hodges via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> On Mon, 20 Jul 2020 at 18:03, codusnocturnus via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> This would be great - just remove the braces, and the .’s, and the struct.
>>
>
> At this point it becomes an argument over syntax [...]
>
> I think it's common to think, "language X has a nice way of expressing Y -
> why don't we do that too?"
> I can be guilty of that just like anyone else.
> But in the end, if a language gives you a way to *express intent*, one
> might wonder whether it's worth expending effort in making syntax nicer for
> niche use cases in favour of providing more utility or fixing design bugs.
>

Richard, apparently your notion of "express intent" doesn't match up with
mine.
When I say that C++ *gives the programmer a way to express his intent*,
what I mean is that C++ provides a specific syntax for that exact intent,
which is intended to be used if and only if that's the programmer's intent.
For example, when I see

    void foo(const std::string& x);

I know that the programmer's *intent* is that `x` should remain unchanged
by a call to `foo`. Now, physically, `foo` could still change `x`, because
of `const_cast`; but in practice the `const` keyword gives me a hint that
in general is really strongly correlated with the programmer's *intent*.

Similarly, if I see an overload set consisting of the signatures

    void foo(int x, int y);
    void foo(int x, int y, bool dx, bool dy);

that's a very strong indication that the programmer *intends* to permit the
caller to call `foo` with either of these signatures, and no others. I
consider overloading a good example of "features that allow us to express
intent."

On the other hand, if I see

    void foo(int x, int y, bool dx=false, bool dy=false);

that's much less of a hint about intent, because it indicates that *either*
the programmer meant to permit a call like `foo(1, 2, true)` or *else* the
programmer doesn't know what they're doing. (In my experience, it's usually
the latter.)

To your specific example: If I see

    struct FooParams { int x, y; bool dx=false, dy=false; };
    void foo(FooParams params);

well, that's basically no hint at all as to what the programmer intends.
Sure, it physically *permits* me to call `foo({1, 2, .dx=true})` or
`foo({})` or `foo(FooParams(1, 2, false, true))` — but the code gives me
practically *zero* indication of what the programmer *intended* me to do!

To me, "a feature that allows me to express intent" would be the exact
*opposite* of "a stack of features that can be house-of-cardsed into
something that physically resembles what I'm trying to do."

Also from C++20: The `concept` keyword doesn't add anything physically
helpful(*) over and above the preexisting house-of-cards that was `inline
constexpr bool fooable_v`; but it does permit programmers to express their
intent better. (* — It adds subsumption, but I don't consider that to be
helpful.)
Constraining a function template with a `requires` clause doesn't add
anything physically helpful(*) over and above the preexisting
`enable_if`/return-type-SFINAE tricks; but it does permit programmers
to *express
their intent* better. (* — It raises the template's overload-resolution
priority, but I don't consider that to be helpful.)
Even C++20's `auto operator<=>(const Me&) = default;` could be considered
to "express intent" better than what came before it.

I'm sympathetic to the idea that programming languages should permit the
programmer to express intent.
I don't think this specific thread has suggested any specific ideas that
would meet that bar w.r.t. named parameters, though. Named parameters are a
very old idea that a *lot* of people have tried and failed to shoehorn into
C++.

–Arthur

Received on 2020-07-20 14:14:21