Live data from Hacker News

Problems with default function arguments (2020)

quuxplusone.github.io

11–20 of 28 posts

Re: Problems with default function arguments (2020)

#11
post #6

All C++ specific though right? Trying to imagine the insanity that would be Numpy (or xyz other Scipy lib) without default function args ...

Yea. And there's even pitfalls the author didn't list. My biggest pet peeve about C++ default arguments it that they are compile time inserted based on the header file. So imagine: void foo(int a = 1) {} Now you ship this in a DLL, but realize the default was bad, so you change it to 2 and ship the new DLL. Well, the 1/2 isn't in the DLL at all . It's only in the header file. So everyone must recompile. Fun times.

Technically that's a subtly incompatible ABI change. ABI changes can be notoriously tricky to spot. That's handled properly on an ELF-based system by bumping the SONAME major (if the library developer knows what they're doing) but that won't help non-ELF platforms or vendored uses.

Of course, the same problem applies in other languages if a constant value changes. A preprocessor macros in C, for example. It's all about non-manifest interfaces and "making things easier" so that any idiot can write software (so they do). Public APIs can be hard when the public is imperfect.

Re: Problems with default function arguments (2020)

#12
post #11
post #6

Earlier quoted context omitted.

Yea. And there's even pitfalls the author didn't list. My biggest pet peeve about C++ default arguments it that they are compile time inserted based on the header file. So imagine: void foo(int a = 1) {} Now you ship this in a DLL, but realize the default was bad, so you change it to 2 and ship the new DLL. Well, the 1/2 isn't in the DLL at all . It's only in the header file. So everyone must recompile. Fun times.

Technically that's a subtly incompatible ABI change. ABI changes can be notoriously tricky to spot. That's handled properly on an ELF-based system by bumping the SONAME major (if the library developer knows what they're doing) but that won't help non-ELF platforms or vendored uses. Of course, the same problem applies in other languages if a constant value changes. A preprocessor macros in C, for example. It's all abo…

> Technically that's a subtly incompatible ABI change

Yea, that's what I said. And it's subtle because of the language making it a foot gun.

> Of course, the same problem applies in other languages if a constant value changes.

That's just C++ the apologist talking.

Re: Problems with default function arguments (2020)

#13
post #4

As someone who does not do modern C++ development, the problems they bring up seem to largely be of the flavor that positional parameters work poorly with default values in C++. The reason being that all default values do is give you the ability to elide the last N arguments. This is much worse than automatic selection of the correct overload based on the supplied arguments. However, it seems to me that you could ent…

Python has supported keyword-only arguments since 3.0, and added positional-only arguments in 3.8+ https://docs.python.org/3/whatsnew/3.8.html#positional-only-...>:

  def name(positional_only_parameters, /, positional_or_keyword_parameters, *, keyword_only_parameters):
      pass
I don’t use Python much these days, and Rust and JavaScript don’t have equivalents, so I haven’t thought much about this, but my gut feeling is that there’s probably never a good reason to support taking an argument by both position and keyword, that it should instead be one or the other. But as I say, I haven’t meditated on this. Curious if any popular linting tool has rules to detect any of these three classes of argument (… and also args and *kwargs).

Re: Problems with default function arguments (2020)

#15

Default arguments are used for sake of the DRY principle. Whenever it is highly likely that the majority of cases of application of a specific function are going to use the same parameter value it can be considered reasonable to imply this parameter value as a default while allowing it to be specified explicitly whenever it make sense to tweak it. Using overloading to implement this pattern would be way more verbose,…

Why would overloading repeat the code? It's typical to do it like that (in Java):

public int doStuff(int a, int b) { //some code }

public int doStuff(int a) { return doStuff(a, 7);} // so b=7 by default.

No need to repeat actual business logic code.

Of course that gets complicated if there are many parameters with default values.

Re: Problems with default function arguments (2020)

#16
post #2

Sure, the pitfalls are real. But they have benefits too. A fair assessment should examine both sides before coming to a decision, not just one. Off the top of my head, these come to mind: - Defaults avoid having to duplicate the rest of the function (everything outside the body braces) just for the sake of an additional parameter. It might not be a big deal for reset() which is simple and only takes one argument anyw…

It's a funny coincidence that your username is "dataflow" when that is exactly what's broken with default arguments: you can't pass the default values around, they can't flow in the code.

If you want to create a proxy function to a function that has default arguments, and want to transparently allow the "default" features to be used from the wrapper function as well, then you have to duplicate the default value in the signature of the wrapper function.

There are other problems, for example due to the nature of function call syntax with positional arguments.

The solution is: Use a struct to hold default values.

    struct FooDefaults
    {
        int arg1 = 3;
        int arg2 = 7;
    }

    void FooFunction(int x, int y, FooDefaults defaults)
    {
        ...
    }

    void usage_code(...)
    {
        int x = 1;
        int y = 2;
        FooDefaults defaults;
        defaults.arg2 = 9;
        FooFunction(defaults);
    }

Re: Problems with default function arguments (2020)

#18

Are there any languages that do not have positional arguments at all and they’re always keyword arguments? My pet peeve is reading code and trying to reason about foo(true, 1, 1, null, “cupcakes”)

I believe in Smalltalk all method arguments are by keyword. Objective-C probably has a similar restriction. I've never written any serious code in either, so I could be wrong.

Re: Problems with default function arguments (2020)

#19
post #2

Sure, the pitfalls are real. But they have benefits too. A fair assessment should examine both sides before coming to a decision, not just one. Off the top of my head, these come to mind: - Defaults avoid having to duplicate the rest of the function (everything outside the body braces) just for the sake of an additional parameter. It might not be a big deal for reset() which is simple and only takes one argument anyw…

It's a funny coincidence that your username is "dataflow" when that is exactly what's broken with default arguments: you can't pass the default values around, they can't flow in the code. If you want to create a proxy function to a function that has default arguments, and want to transparently allow the "default" features to be used from the wrapper function as well, then you have to duplicate the default value in th…

   void FooFunction(int x, int y, optional optarg1 = {}, optional optarg2 = {}) {
     int arg1 = optarg1.value_or(3);
     int arg2 = optarg1.value_or(7);
   }

   void usage_code() {
     FooFunction(x, y, {}, 9);
   }
I.e. for complex interfaces defaulted arguments should default to an out-of-band placeholder, not to the actual value.

I do like the struct as well, but it is still not ideal if you want to use initializers. I.e this doesn't work in C++:

   FooFunction(x, y, {.arg2 = 9});
You have to specify all preceding values FooFunction(x, y, {.arg1 = 2, .arg2 = 9});

Works better with optional (and converting everything to a struct):

   FooFunction({.x = x, .y=x, .arg1 = nullopt, .arg2 = 9});

Re: Problems with default function arguments (2020)

#20

Are there any languages that do not have positional arguments at all and they’re always keyword arguments? My pet peeve is reading code and trying to reason about foo(true, 1, 1, null, “cupcakes”)

Not a language, but I have seen some recent editor integration with LSP stuff that will visually display that as

        foo(bake: true, batches: 1,  boxes:1, frosting: null, type: “cupcakes”)
Post reply on HN