On Sun, Oct 24, 2021 at 01:16:02PM +1100, Chris Angelico wrote:

> What I'm more often seeing is cases that are less obviously a
> late-binding, but where the sentinel is replaced with the "real" value
> at the point where it's used, rather than up the top of the function.

Got any examples you can share?

And is it really a problem if we delay the late-binding to the point 
where the value is actually needed? Here's a toy example:

    # Using only early binding.
    def function(spam, eggs=None, cheese=None):
        if eggs is None:
            eggs = cheap_default()
        # do stuff using eggs
        ...
        if condition:
            return result
        if cheese is None:
            cheese = expensive_default()
        # do stuff using cheese
        ... 
        return result


The cheese parameter only gets used if the processing of spam with eggs 
fails to give a result. But if cheese is used, the default is expensive. 
Is it really a problem if we delay evaluating that default to the point 
where it is needed?

So this would be another example where automatic late-binding wouldn't 
be used. If the default is very expensive, I would stick to manual late- 
binding using None, and only evaluate it as needed.

Maybe this is an argument for some sort of thunk, as in Algol, which is 
only evaluated at need. Then we could just write:

    # Assume late-binding with thunks.
    def function(spam, eggs=cheap_default(), cheese=expensive_default()):
        # do stuff using eggs
        ...
        if condition:
            return result
        # do stuff using cheese
        ... 
        return result
    
and the thunks `cheap_default()` and `expensive_default()` will only be 
evaluated *if they are actually needed*, rather than automatically 
when the function is called.

To be clear about the semantics, let me illustrate. I am deliberately 
not using any extra syntax for late-binding.

    # early binding (the status quo)
    def func(arg=expression): ...


The expression is evaluated when the def statement is run and the func 
object is created.


    # late binding (minus any extra syntax)
    def func(arg=expression): ...

The expression is evaluated eagerly when the function is called, if and 
only if the parameter arg has not been given a value by the caller.


    # late binding with thunk
    def func(arg=expression): ...

The expression is evaluated only if and when the body of the function 
attempts to use the value of arg, if the caller has not provided a 
value. So if the function looks like this:

    # late binding with a thunk that delays execution until needed
    def func(flag, arg=1/0):
        if flag:
            print("Boom!")
            return arg
        return None

then func(True) will print Boom! and then raise ZeroDivisionError, and 
func(False) will happily return None.

I have no idea whether thunk-like functionality is workable in Python's 
execution model without slowing down every object reference, but if it 
is possible, there could be other really nice use-cases beyond just 
function defaults.


-- 
Steve
_______________________________________________
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/NW63MNKO7JFQLTP7LTSJQP7W5JB72DGN/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to