On Sun, Oct 24, 2021, 10:11 AM Chris Angelico 
Not sure I understand. Your example was something like:

def fn2(thing):
    a, b = 13, 21
    x = 5
    print("Thing is:", thing)

def f(x=defer: a + b):
    a, b = 3, 5
    fn2(defer: x)
    return x

So inside f(), "defer: a + b" will look in f's scope and use the
variables a and b from there, but passing a "defer: x" to fn2 will use x from f's scope, and then a and b from fn2's?

Yes, basically as you describe. A "deferred object" is basically just a string that knows to wrap itself in eval() when accessed (maybe Steven's "thunk" is a better term... But definitely something first-class, unlike in Algol).

So within fn2() the parameter 'thing' is bound to an object like '<defer eval("a+b")>'.  

Indeed this means that the 'defer:' or 'thunk' or special symbol spelling, has to decide whether the thing being deferred is already itself a deferred object. If so, just pass along the identical object rather than treat it as a new expression.

At least that's what would feel most intuitive to me. But I'm deliberately not pushing a specific syntax. For example, if we didn't want to "reuse" the spelling 'defer:' for both creating and passing a deferred object, we could have different spellings.

def f(x=defer: a + b):
    a, b = 3, 5
    fn2(noeval: x)
    return x

... I don't like that spelling, but just showing the concept.