# HG changeset patch # User Paul Boddie # Date 1386170240 -3600 # Node ID d148464ebb37944496e94463beebd8744fea69cb # Parent c53bbfded454f713326b3205459e3b1d08d4f2aa Added a discussion of closures, defaults and dynamic functions. diff -r c53bbfded454 -r d148464ebb37 docs/closures.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/closures.txt Wed Dec 04 16:17:20 2013 +0100 @@ -0,0 +1,100 @@ +Micropython does not support closures, but the way they might be supported is +to provide outer namespaces as implicit parameters, somewhat like default +parameters, to functions in a way that is more or less supported by "dynamic" +functions (which provide attributes to record additional state). + +Consider the following example: + +def outer(x): + def inner(y): + return x + y + return inner + +f = outer(10) # f -> inner +x = f(5) # x -> 15 + +This could be represented as follows: + +def outer(x): + # refers to the local namespace + def inner(y, =): + return .x + y + return inner + +In other words, where a function uses names from another namespace, usage of +such names must be qualified. Note that outer must be "dynamic" itself to +support external access to its contents, and all accessed names must be +exported by the dynamic portion of the function namespace. + +What this means is that outer must be dynamically allocated, at least as far +as its exported portion is concerned, and inner must also be dynamically +allocated as far as the reference to outer is concerned. + +Defaults and Allocation +----------------------- + +The above is somewhat similar to how defaults are supported, and this is +illustrated as follows: + +def outer(x): + def inner(y, x=x): + return x + y + return inner + +This needs to be represented as follows: + +def outer(x): + def inner(y, .x=x): + return .x + y + return inner + +Here, any invocation of the returned function can omit the x argument, and +this argument is actually acquired from the instance of the inner function +returned by outer. However, this freezes the value of x during the invocation +of outer, whereas a closure has the "default" as the outer namespace and +accesses x through that namespace, picking up any modifications that may have +occurred subsequently. + +Here is an example of differing behaviour between closures and "static" +defaults: + +def outer(x): def outer(x): + def inner(y): def inner(y, x=x): # stores x + return x + y return x + y # gets the stored value + x += 1 x += 1 # changes x only in outer + return inner return inner + +Closures and Allocation +----------------------- + +In principle, the support for closures would follow from the support for +defaults, but instead of specific names being populated in dynamic functions, +entire namespaces would be referenced. + +In addition, the mechanics of accessing referenced namespaces would involve a +special parameter for accessing the dynamic state, just as is done with +defaults. + +def outer(x): + = namespace(x=x) # allocate the locals dynamically + + def inner(y, ): # the special parameter exposes state + return ..x + y # and thus the outer namespace + + .x += 1 + return makedynamic(inner, =) + +Note that the outer function, unlike a function that has externally set +defaults, must have its local namespace dynamically allocated upon every +invocation. In comparison, a dynamic function providing defaults needs only to +be allocated when its definition is evaluated. + +When the dynamic inner function is called, its state is provided through the +special parameter: + +f = outer(10) # returns inner with extra state referencing +f(5) # invokes inner with y=5, =state + # evaluates ..x + # which is .x + # which is x=11 + # returning 16