
On Sun, Oct 31, 2021 at 12:17 PM Brendan Barnwell <brenbarn@brenbarn.net> wrote:
On 2021-10-30 16:12, Chris Angelico wrote:
Increasingly it seems to me as if you are placing inordinate weight on the idea that the benefit of default arguments is providing a "human readable" description in the default help() and so on. And, to be frank, I just don't care about that. We can already provide human-readable descriptions in documentation and we should do that instead of trying to create gimped human-readable descriptions that only work in special cases. Or, to put it even more bluntly, from my perspective, having help() show something maybe sort of useful just in the case where the person wrote very simple default-argument logic and didn't take the time to write a real docstring is simply not a worthwhile goal.
Interesting. Then why have default arguments at all? What's the point of saying "if omitted, the default is zero" in a machine-readable way? After all, you could just have it in the docstring. And there are plenty of languages where that's the case.
The point of default arguments is to allow users of the function to omit arguments at the call site. It doesn't have anything to do with docstrings.
That's optional parameters. It's entirely possible to have optional parameters with no concept of defaults; it means the function can be called with or without those arguments, and the corresponding parameters will either be set to some standard "undefined" value, or in some other way marked as not given.
Or do you mean why not just have all omitted arguments set to some kind of "undefined" value and then check each one in the body of the function and replace it with a default if you want to? Well, for one thing it makes for cleaner error handling, since Python can tell at the time of the call that a required argument wasn't supplied and raise that error right away.
That's the JavaScript way - every parameter is optional. But it's entirely possible to have some mandatory and some optional, while still not having a concept of defaults. Python, so far, has completely conflated those two concepts: if a parameter is optional, it always has a default argument value. (The converse is implied - mandatory args can't have defaults.)
It's sort of like a halfway type check, where you're not actually checking that the correct types of arguments were passed, but at least you know that the arguments that need to be passed were passed and not left out entirely.
Given that Python doesn't generally have any sort of argument type checking, that's exactly what we'll get by default.
For another thing, it does mean that if you know the default at the time you're defining the function, you can specify it then. What you can't do is specify the default if you don't know the default at function definition time, but only know "how you're going to decide what value to use" (which is a process, not a value).
Right. That's the current situation.
I'm of the opinion that having more information machine-readable is always better. Are you saying that it isn't? Or alternatively, that it's only useful when it fits in a strict subset of constant values (values that don't depend on anything else, and can be calculated at function definition time)?
Now wait a minute, before you said the goal was for it to be human readable, but now you're saying it's about being machine readable! :-)
Truly machine readable is the best: any tool can know exactly what will happen. That is fundamentally not possible when the default value is calculated. Mostly machine readable means that the machine can figure out what the default is, even if it doesn't know what that means. My proposal (not in the reference implementation as yet) is to have late-bound defaults contain a marker saying "the default will be len(a)", even though the "len(a)" part would be just a text string.
What I am saying is that there is a qualitative difference between "I know now (at function definition time) what value to use for this argument if it's missing" and "I know now (at function definition time) *what I will do* if this argument is missing". Specifying "what you will do" is naturally what you do inside the function. It's a process to be done later, it's logic, it's code. It is not the same as finalizing an actual VALUE at function definition time. So yes, there is a qualitative difference between:
# this if argument is undefined: argument = some_constant_value
# and this if argument is undefined: # arbitrary code here
I mean, the difference is that in one case arbitrary code is allowed! That's a big difference.
Right. That is a very real difference, which is why there is a very real difference between early-bound and late-bound defaults. But both are argument defaults.
Based on some of your other posts, I'm guessing that what you mean about machine readability is that you appreciate certain kinds of labor-saving "self-documentation" techniques, whereby when we write the machine-readable code, the interpreter automatically derives some human-readable descriptions for stuff. For instance when we write `def foo` we're just defining an arbitrary symbol to be used elsewhere in the code, but if we get an exception Python doesn't just tell us "exception in function number 1234" or the line number, but also tells us the function name.
And yeah, I agree that can be useful. And I agree that it would be "nice" if we could write "len(a)" without quotes as machine-readable code, and then have that stored as some human-readable thing that could be shown when appropriate. But if that's nice, why is it only nice in function arguments? Why is it only nice to be able to associate the code `len(a)` with the human-readable string "len(a)" just when that string happens to occur in a function signature?
It would be very nice to have that feature for a number of places. It's been requested for assertions, for instance. If that subfeature becomes more generally available, the language will be the richer for it.
So yes, it's true that adding convenience functions to derive human-readable forms from machine-readable code is handy, but it's not ALWAYS automatically good regardless of other considerations, and I don't see that it outweighs the costs here. The benefit of autogenerating the string "len(a)" from the argument spec isn't quite zero but it's pretty tiny.
It's mainly about writing expressive code, which can then be interpreted by humans AND machines. It's about writing function defaults as function defaults, not working around a technical limitation. It's about writing function headers such that they have what function headers should have, allowing the function body to contain only the function body. We could just write every function with *args, **kwargs, and then do all argument checking inside the function. We don't do this, because it's the job of the function header to manage this. It's not the function body's job to replace placeholders with actual values when arguments are omitted. ChrisA