Why are default arguments evaluated at definition time?

The alternative would be quite heavyweight — storing “default argument values” in the function object as “thunks” of code to be executed over and over again every time the function is called without a specified value for that argument — and would make it much harder to get early binding (binding at def time), which is often what you want. For example, in Python as it exists:

def ack(m, n, _memo={}):
  key = m, n
  if key not in _memo:
    if m==0: v = n + 1
    elif n==0: v = ack(m-1, 1)
    else: v = ack(m-1, ack(m, n-1))
    _memo[key] = v
  return _memo[key]

…writing a memoized function like the above is quite an elementary task. Similarly:

for i in range(len(buttons)):
  buttons[i].onclick(lambda i=i: say('button %s', i))

…the simple i=i, relying on the early-binding (definition time) of default arg values, is a trivially simple way to get early binding. So, the current rule is simple, straightforward, and lets you do all you want in a way that’s extremely easy to explain and understand: if you want late binding of an expression’s value, evaluate that expression in the function body; if you want early binding, evaluate it as the default value of an arg.

The alternative, forcing late binding for both situation, would not offer this flexibility, and would force you to go through hoops (such as wrapping your function into a closure factory) every time you needed early binding, as in the above examples — yet more heavy-weight boilerplate forced on the programmer by this hypothetical design decision (beyond the “invisible” ones of generating and repeatedly evaluating thunks all over the place).

In other words, “There should be one, and preferably only one, obvious way to do it [1]”: when you want late binding, there’s already a perfectly obvious way to achieve it (since all of the function’s code is only executed at call time, obviously everything evaluated there is late-bound); having default-arg evaluation produce early binding gives you an obvious way to achieve early binding as well (a plus!-) rather than giving TWO obvious ways to get late binding and no obvious way to get early binding (a minus!-).

[1]: “Although that way may not be obvious at first unless you’re Dutch.”

Leave a Comment