We can achieve even more expressive power in our programs by creating functions whose returned values are themselves functions. An important feature of lexically scoped programming languages is that locally defined functions maintain their parent environment when they are returned. The following example illustrates the utility of this feature.

Once many simple functions are defined, function composition is a natural method of combination to include in our programming language. That is, given two functions f(x) and g(x), we might want to define h(x) = f(g(x)). We can define function composition using our existing tools:

>>> def compose1(f, g):
        def h(x):
            return f(g(x))
        return h

The environment diagram for this example shows how the names f and g are resolved correctly, even in the presence of conflicting names.

1def square(x):
2    return x * x
3
4def successor(x):
5    return x + 1
6
7def compose1(f, g):
8    def h(x):
9        return f(g(x))
10    return h
11
12def f(x):
13    """Never called."""
14    return -x
15
16square_successor = compose1(square, successor)
17result = square_successor(12)
End
line that has just executed

next line to execute

Global
square
 
successor
 
compose1
 
f
 
square_successor
 
result169
f1: compose1 [parent=Global]
f
 
g
 
h
 
Return
value
 
f2: h [parent=f1]
x12
Return
value
169
f3: successor [parent=Global]
x12
Return
value
13
f4: square [parent=Global]
x13
Return
value
169
func square(x) [parent=Global]
func successor(x) [parent=Global]
func compose1(f, g) [parent=Global]
func f(x) [parent=Global]
func h(x) [parent=f1]

The 1 in compose1 is meant to signify that the composed functions all take a single argument. This naming convention is not enforced by the interpreter; the 1 is just part of the function name.

At this point, we begin to observe the benefits of our effort to define precisely the environment model of computation. No modification to our environment model is required to explain our ability to return functions in this way.

Video