Background

Definition

i
def same(num):
    return num
i
["func", ["num"], ["get", "num"]]

Saving Functions

i
["set", "same", ["func", ["num"], ["get", "num"]]]

Anonymous Functions

i
double = lambda x: 2 * x
double(3)

Implementing Call

i
["call", "same", 3]
  1. Evaluate arguments.

  2. Look up the function.

  3. Create a new environment.

  4. Call do to run the function's action and captures the result.

  5. Discard environment created in step 3.

  6. Return the result.

Eager and Lazy

The Environment

Implementing Definition

i
def do_func(env, args):
    assert len(args) == 2
    params = args[0]
    body = args[1]
    return ["func", params, body]

Implementing Call

i
def do_call(env, args):
    # Set up the call.
    assert len(args) >= 1
    name = args[0]
    values = [do(env, a) for a in args[1:]]

    # Find the function.
    func = env_get(env, name)
    assert isinstance(func, list) and (func[0] == "func")
    params, body = func[1], func[2]
    assert len(values) == len(params)

    # Run in new environment.
    env.append(dict(zip(params, values)))
    result = do(env, body)
    env.pop()

    # Report.
    return result

A Test

i
["seq",
  ["set", "double",
    ["func", ["num"],
      ["add", ["get", "num"], ["get", "num"]]
    ]
  ],
  ["set", "a", 1],
  ["repeat", 4, ["seq",
    ["set", "a", ["call", "double", ["get", "a"]]],
    ["print", ["get", "a"]]
  ]]
]
i
2
4
8
16
=> None

Dynamic Scoping

i
["seq",
  ["def", "lower", [], ["get", "x"]],
  ["def", "one", [], ["seq", ["set", "x", 1], ["call", "lower"]]],
  ["def", "two", [], ["seq", ["set", "x", 2], ["call", "lower"]]],
  ["print", ["call", "one"]],
  ["print", ["call", "two"]]
]
i
1
2
=> None

Lexical Scoping

Closures

i
def make_hidden(thing):
    def _inner():
        return thing
    return _inner

has_secret = make_hidden(1 + 2)
print("hidden thing is", has_secret())
i
hidden thing is 3

A More Useful Example

i
def make_adder(to_add):
    def _inner(value):
        return value + to_add
    return _inner

adder_func = make_adder(100)
print(adder_func(1))
i
101
Closures
Figure 1: Closures

Objects

i
def make_object(initial_value):
    private = {"value": initial_value}

    def getter():
        return private["value"]

    def setter(new_value):
        private["value"] = new_value

    return {"get": getter, "set": setter}

object = make_object(00)
print("initial value", object["get"]())
object["set"](99)
print("object now contains", object["get"]())
i
initial value 0
object now contains 99

Objects

Objects as closures
Figure 2: Implementing objects using closures

Summary

Concept map of functions and closures
Figure 3: Concept map.