On Fri, May 29, 2020 at 12:36:20PM +1200, Greg Ewing wrote: > On 29/05/20 12:17 am, Richard Damon wrote: > >But default values for arguments are really part of the responsibility > >for the caller, not the called function. The classic implementation > >would be that the caller passes all of the explicitly, > > I would say that's really a less-than-ideal implementation, > that has the consequence of requiring information to be put > in the header that doesn't strictly belong there.
"Strictly" according to whom? Well, you obviously :-) For 30 years, Python's early bound default arguments have been part of the function signature. It's only late bound default arguments which are hidden inside the body. So you are arguing that for 30 years Python has put information into the function header that doesn't belong there. Where else would you put the parameter defaults, if not in the parameter list? > To my mind, the signature consists of information that a > static type checker would need to verify that a call is valid. A static type checker would have to know whether a parameter has a default value, even if it doesn't know what that default value is. So you are arguing that the ideal (using your word from above) function signature for, let's say, the `open` builtin should look something like this: open(file, mode= <REDACTED>, buffering= <REDACTED>, encoding= <REDACTED>, errors= <REDACTED>, newline= <REDACTED>, closefd= <REDACTED>, opener= <REDACTED>) plus type information, which I cannot be bothered showing. This ideal arrangement tells the static type checker everything it needs to know to determine whether a call is valid or not: - the name and order of the parameters; - their types (pretend I showed them); - whether they can be omitted or not; - whether or not the function takes positional only parameters (in this case, it doesn't); - whether or not the function takes vararg and keyword varargs as well (in this case, it doesn't); but none of the things that it doesn't need to know, such as the actual default value. [Aside: well, *almost* everything: it doesn't tell the static checker whether the function is in scope or not.] As a user of that function, I need to know the same things the static checker needs to know, *plus* the actual default values. I'm not sure why you care more about the type checker than the users of the function. Even if you couldn't care less about my needs, presumably you are a user of the function too. > That does not include default values of arguments. The fact > that a particular value is substituted if an argument is > omitted is part of the behaviour of the function, not part > of its signature. That depends on what you define as the *function* signature and whether you equate it with the function's *type* signature. (Or dare I say it, whether you conflate it with the type signature.) Consider two almost identical functions: def add(a:int, b:int=0)->str: return a+b def add(a:int, b:int=1)->str: return a+b These two functions have: - identical names; - identical parameter lists; - identical type signatures (int, int -> int); - identical function bodies; but they are different functions, with similar but different semantics (the first defaults to a no-op, the second doesn't). Where does the difference lie? It's not specifically in the implementation (the body), that's identical. It's not in the *type* signature, we agree on that. I want to say that it is a difference in the *function* signature, which consists of not just the types, but also the function name and parameters including defaults. Not only is this a useful, practical way of discussing the difference, it matches 30 years of habit in the Python community. Reading your post is literally the first time it has dawned on me that anyone would want to exclude default values from the notion of function signature. (By the way, in Forth, function signatures are comments showing stack effects, and they're only for the human reader.) > By putting default values in the header, we're including a > tiny bit of behavioural information. Behavioural information is already present as soon as the header includes whether or not parameters can be, or must be, given by name or position, and whether or not it accepts varargs. In languages with procedures (or void functions) as well as functions, that behavioural information is also present in the signature. In languages with checked exceptions, that behavioural information is likewise present in the signature. What's your problem with these? > By distinguishing between > one-time and per-call evaluation of defaults, we're adding a > bit more behavioural information. Yes. > Where do we draw the line? How much of the behaviour of the > function do we want to move into the header? Well obviously we don't draw the line until we've fallen down the slippery slope and the entire body of the function is part of the header! *wink* I think there are serious practical difficulties in moving much more behavioural information into the header. Not that there's much more we might want in the function header. Checked exceptions perhaps? Not me personally. What else *could* we move into the header? -- Steven _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/JODBVL4L5K7DA3WTB4QSV4JVHSSJRJVT/ Code of Conduct: http://python.org/psf/codeofconduct/