![](https://secure.gravatar.com/avatar/5426055f54148a02d5d724ccbfdeca19.jpg?s=120&d=mm&r=g)
On 2021-10-24 at 13:23:51 +1100, Steven D'Aprano <steve@pearwood.info> wrote:
On Sat, Oct 23, 2021 at 02:54:54PM -0700, 2QdxY4RzWzUUiLuE@potatochowder.com wrote:
[...]
The function header is a syntactic construct - the "def" line, any decorators, annotations, etc.
If you mean that def statements and decorators run at compile time, then I agree. If you mean something else, then I don't understand.
Pedantic note: def statements and decorators run at runtime, the same as all other statements. (Including class statements.)
Yep. My mistake.
I can't make the leap to claiming that late binding is part of defining the function's arguments. You say "late binding of function arguments"; I say "the part of the function that translates the arguments into something useful for the algorithn the function encapsulates."
Consider what happens when you call a function now:
bisect(alist, obj)
[good explanation of what happens with default parameters snipped]
Now consider what would happen if we used late binding. Everything would be the same, *except* that instead of fetching a static reference to a pre-existing object, the interpreter would have to fetch a reference to some code, evaluate the code, and use *that* object as the default.
There is no reason to consider that part of the function body, it is still performed by the interpreter.
It is only habit from 30 years of working around the lack of late- binding defaults by putting the code inside the function body that leads us to think that late-binding is necessarily part of the body.
In Python today, of course late-binding is part of the body, because that's the only way we have to delay the evaluation of an expression ...
Aha. I see it now (and it's not just those 30 years of Python, it's the previous decade of Basic, FORTRAN, Pascal, C, etc.). My first impulse remains that all those things "the interpreter" does with default values are still part of the function, and that the shorthand declarative syntax is still just sugar for the explicit logic.
But consider languages which have late-binding, I think Smalltalk and Lisp are the two major examples. I'm not an expert on either, but I can read StackOverflow and extrapolate a meaning to code :-)
(defun test1 (&optional (x 0)) (+ x x))
is a function that takes one argument with a default value of 0. Lisp uses late-binding: the default value is an expression (an S-expression?) that is evaluated when the code is called, not at compile time, but it is not part of the body of the function (the `(+ x x)` expression.
Yep. I've written some non-toy Lisp code, and perhaps because of the language in Lisp documentation ("initial forms" rather than "default values"), I definitely see all of that binding as part of the function, whether I write it in the body or in the lambda list. (FWIW, there's also a 3-tuple form of optional parameter, where the third element is a predicate that is bound to a boolean value that indicates whether the value of the argument came from the caller or the default value (thus eliminating the need for unique sentinels). If you're so inclined, find "supplied-p-parameter" on <http://www.lispworks.com/documentation/HyperSpec/Body/03_da.htm>.) As I said before, pushing this sort of logic into "late binding" scratches an itch I don't have. I gladly write two (or more) [public] functions that call a common [possibly private] function rather than one [public] function with optional arguments and default values. Easy to write; easy to read; and easy to test, maintain, and extend. Yes, theoretically, the number of functions grows exponentially, but rarely do I need more than a few such glue functions to cover most (if not all) of the real use cases.