[Python-ideas] A "local" pseudo-function

Steven D'Aprano steve at pearwood.info
Tue May 1 14:00:23 EDT 2018


On Tue, May 01, 2018 at 09:26:09PM +1200, Greg Ewing wrote:
> Steven D'Aprano wrote:
> >So what was the closure? If the surrounding function was still running, 
> >there was no need to capture the running environment in a closure?
> 
> You seem to be interpreting the word "closure" a bit
> differently from most people. It doesn't imply anything
> about whether a surrounding function is still running or
> not.
> 
> A closure is just a piece of code together with a runtime
> environment.

That's not *all* it is, because obviously *every* function has both a 
piece of code and a runtime environment.

y = 1
def func():
    x = 2
    return x+y

Here, there's a local environment as well as an implicit global one. 
Surely we don't want to call this a closure? If we do, then all 
functions are closures and the term is just a synonym for function.

Wikipedia gives a definition:

    a closure (also lexical closure or function closure) is a 
    technique for implementing lexically scoped name binding
    in a language with first-class functions. Operationally,
    a closure is a record storing a function together with an
    environment.

https://en.wikipedia.org/wiki/Closure_%28computer_programming%29

but also warns 

    As different languages do not always have a common definition
    of the lexical environment, their definitions of closure may
    vary also

and explicitly says that Pascal (at least standard Pascal) didn't do 
closures:

    Traditional imperative languages such as Algol, C and Pascal
    either do not support nested functions (C) or do not support
    calling nested functions after the enclosing function has
    exited (GNU C, Pascal), thus avoiding the need to use closures.


Neal Gafter gives what I believe is *the* standard definition of 
closures, at least in academic and functional programming circles, 
probably taken from Guy Steele and Scheme:

    A closure is a function that captures the bindings of free
    variables in its lexical context.

http://gafter.blogspot.com.au/2007/01/definition-of-closures.html

Since Steele basically wrote the book on closures, I think we ought to 
take his definition seriously :-)

(That's also the definition I was thinking of.)

By this definition, if there are no free variables, or no captured 
bindings, then its not a closure. func() above isn't a closure, since 
"x" isn't a free variable, and while "y" is, the value of it isn't 
captured.

Unfortunately, the terminology has become a real mess, especially since 
the technique has moved into languages like Ruby and Javascript. Martin 
Fowler declares that closures, lambdas and blocks are the same thing:

https://martinfowler.com/bliki/Lambda.html

although he does admit that technically you can have lambdas that aren't 
closures. I think Fowler is abusing the terminology since all three of 
his terms are orthogonal:

- closures can be created with def or lambda and are not 
  necessarily anonymous;

- lambda comes from the lambda calculus, where it refers to 
  anonymous function expressions, not specifically whether
  they capture the value of free variables in their environment;

- "block" typically refers to the structure of the source code; 
  in Ruby it also refers to a kind of first-class anonymous
  callable object. Such blocks may, or may not, contain free
  variables.


At least I don't use this definition of closure:

    "a closure is a callback that Just Works."

https://reprog.wordpress.com/2010/02/27/closures-finally-explained/

*wink*



> In typical Pascal implementations, a closure
> is represented by a (code_address, frame_pointer) pair,
> where the frame_pointer points to a chain of lexically
> enclosing stack frames. The language rules make it possible
> to manage the frames strictly stack-wise, which simplifies
> the memory management, but that doesn't make the closure
> any less of a closure.

Well... it's a pretty feeble closure, since Pascal doesn't support 
functions as first-class values. More like second-class.

The c2 wiki describes Pascal:

    However, when a function is passed to another function
    and later called, it will execute in the lexical context
    it was defined in, so it is, IN SOME SENSE [emphasis
    added], "closed over" that context.

http://wiki.c2.com/?LexicalClosure

but I think that sense is the same sense that func() above is "closed 
over" the free value y. The loosest possible sense.

If we accept that "Pascal has closures", they're pretty crap closures, 
since they don't let you do any of the interesting and useful things you 
can do in languages with "real" closures like Scheme and Python.

And because it is based on an implementation detail (as you say, it is 
only *typical*, not mandatory, for Pascal inner functions to be 
implemented this way), we have to consider what happens if we come 
across a Pascal implementation which *doesn't* use a (code_address, 
frame_pointer) pair. Is that no longer Pascal?

This is the interesting, and frustrating, thing about definitions: 
deciding where the boundaries lie, and we could argue the boundaries 
for days without getting anywhere :-)



-- 
Steve


More information about the Python-ideas mailing list