Closures (programming)

From Helpful
Jump to navigation Jump to search
Some fragmented programming-related notes, not meant as introduction or tutorial

Data: Numbers in computers ·· Computer dates and times ·· Data structures

Wider abstractions: Programming language typology and glossary · Generics and templating ·· Some abstractions around programming · · Computational complexity theory notes · Synchronous, asynchronous · First-class citizen

Syntaxy abstractions: Constness · Memory aliasing · Binding, assignment, and such · Hoisting · Closures · Context manager · Garbage collection

Language specific: Python notes ·· C and C++ notes · Compiling and linking ·· Lua notes

Algorithms: Dynamic programming · Sorting · String search · Sequence alignment and diffs

Sharing stuff: Communicated state and calls · Locking, data versioning, concurrency, and larger-scale computing notes ·· Dependency hell

Design concepts: Entanglement, Decoupling; Information Hiding, Inversion · Design patterns

Teams and products: Programming in teams, working on larger systems, keeping code healthy · Benchmarking, performance testing, load testing, stress testing, etc. · Maintainability

More applied notes: Optimized number crunching · OS-level notes · File polling, event notification · Webdev · GUI toolkit notes · StringBuilder


Mechanics of duct taping software together: Automation, remote management, configuration management · Build tool notes · Packaging · Installers


This is about the programming concept - not mathematical closures, physhological closure, or any other kind.


Definition

Closures are easy enough to define technically, but harder to explain usefully.


Say, Foldoc says "a closure is a data structure that holds an expression and an environment of variable bindings in which that expression is to be evaluated. The variables may be local or global."


Even if you've reread that a few times, or even already understand closures, that is still not very clear. So:

Technical side

In a lot of cases, the rough intuition for a closure is a function that remembers (part of) the scope it was defined in.

...which matters only when a function can be used in other scopes, so this is mostly a thing where functions/lambdas are first class citizens, i.e. things that can be handed around.


Example still on the fairly technical side (skip if you want something more practical)

Consider the following pseudocode (valid javascript, but pretend you don't know that, or whether it supports closures):

var add = function(a) {
  return function(b) {
    return a + b;
  }
}

//create new function
var add10 = add(10);

//test it:
add10(5);   // 15, yay


Note that a is declared in the outer function's scope (by being a parameter on it, but that doesn't really make a difference).

The inner function gets access to a because local/nested functions gets its own scope, but also to see their parent's function's scope. (which is often half the reason nested functions themselves are useful).


On top of that, the above is apparently a language that allows you to return functions.

Which means it needs to deal with the possibility that a function can get handed out of the scope it was defined in. Above, we've handed that inner function outside.


What would that do to the scopes involved? In this case, after that inner function is returned and handed out, how does it find a?

(There's a few different ways you could argue that should work. It is an interesting exercise to work them out, if you care to learn more about how scope works in different execution models)

Say, a language that allows nested function but not closures would probably consider this invalid code (the precise reasons would vary a little with how exactly that languages thinks about scope).


A fairly sensible alternative is to say something like "everything that is in scope while a function was defined, will be available when you call add from anywhere else."

In the example, that means the anonymous inner function inside add will get a reference to a.

Which means the above example is working Javascript code.


(You may notice that this is not the best example for when closures are useful. If the language supported function nesting but not closures, you could get around it just by handing a into the inner function explicitly, thereby binding it into the inner function's scope. Not the cleanest solution if you have a lot of them, but certainly workable.)


Examples

Javascript uses

Python use

Closures exist in python since 2.2.


Example: if you do a lot of sorting

One nice thing it allows is sorting an array-in-array structure by arbitrary columns, without having to hardcode for each specific case.

Say you do a lot of sorts on different things, and find yourself typing things like:

matrixish.sort( lambda a,b: cmp(a[2],b[2]) )


Instead, you can

# define this once
def nth_column_lex(n):
    return lambda a,b: cmp(a[n],b[n])

# and use it any amount of times like:
matrixish.sort( nth_column_lex(2) )


Example: if you do a lot of filtering

You can use python closures to support syntactic sugar such as:

def paid_more_than(amount):
    amount += 10 # ...cheapskate hack
    return lambda item: item['salary'] > amount

fair      = paid_more_than( 140 )  
expensive = paid_more_than( 160 )
# fair and expensive  are functions that return true or false, so can be used in filter()

employees=[ {'salary':150}, {'salary':170,'isManager':True}, {'salary':120}  ]
print(  filter( fair,      employees )  )
print(  filter( expensive, employees )  )

fair and expensive are newly created functions (here via lambda because it's brief that way), and their variable 'amount' is based on the value that it had in its defining context - which is the respective executions of paid_more_than.

This makes them independent (no side effects, e.g. no outside alterations of amount is possible).

This also means you can use them in list comprehensions and generator functions (ignore them if you don't know them yet; think of it them as list creators-and-filters):

result = [e  for e in employees  if fair(e)]

Note that fair(e) is more or less like saying paid_more_than(140)(e), except that that would create a closure function every execution.


Since the non-generator/non-closured alternative to this is basically a function with a for loop, it's short through closures, though only if you actually use the paid_more_than thing regularly.

Of course, the above example is a little silly by being one-shot, as the for loop:

amount=140
for e in employees:
    if e['salary']>amount:
        print e

...is good enough.

Possible caveats

Read-only scope issues

Nested closures

Easier memory leaks