Search Top Index
HELP CLOSURES A. Sloman Jan 1991 CONTENTS - (Use <ENTER> g to access required sections) -- Introduction -- Creating closures -- Protected closures -- An example -- Examining closures -- Closures of traced procedures -- How closures work -- Notes -- See also: -- Introduction ------------------------------------------------------- Closures are procedures that are created from other procedures by means of 'partial application'. The new procedure may include some data or frozen arguments, which are given to the original procedure when the closure is run. -- Creating closures -------------------------------------------------- Closures are created explicitly in three different ways 1. using 'decorated brackets' proc(%arg1,arg2,...%) (see HELP * PERCENT). 2. using partapply(<procedure>, <list>) (See HELP * PARTAPPLY) 3. using consclosure(<procedure>, <arg1>,<arg2>,...<argN>,N) (See REF * PROCEDURE) Type 1 compiles to a call of -consclosure-, i.e. type 3. There are also closures created implicitly by procedures that return nested procedures binding values of non-local lexical variables, as described (with examples) in HELP * LVARS. These are called "lexical" closures. (Full technical details are in REF * VMCODE). All closures are a combination of procedure and some (alterable) data on which the procedure works. Like ordinary procedures they have *PDPROPS, *PDNARGS and can have updaters. -- Protected closures ------------------------------------------------- Some closures (e.g. those produced via non-local lexical identifiers) are "protected". A protected closure is unalterable, i.e. its frozen values, PDNARGS and PDPART cannot be updated. * ISCLOSURE returns 1 for such closures. -- An example --------------------------------------------------------- A fairly common use of partial application to create closures is to take a two-argument procedure, like -member- and then create a closure of it with some data "frozen" in to be used as the second argument. For example, using method 1 above, we can create a procedure called -iscolour- that recognizes colour names, thus: vars iscolour Then iscolour("green") => ** <true> iscolour("pink") => ** <false> From version 14 Poplog it has been possible to define this as follows: define iscolour = member(%[red orange yellow green blue indigo violet]%) enddefine; which has the advantage that VED operations like ved_mcp, ved_lcp, etc will work on this. If a procedure is partially applied to more arguments than it requires, then on every call it will leave the surplus items on the user stack. This may be a source of bugs. Equally it can be useful. For example, to define a procedure that always returns the number 99 partially apply identfn to 99: define return99 = identfn(%99%) enddefine; return99() => ** 99 An example of identfn partially applied to two arguments can be found in the definition of read_typespec in LIB * TYPESPEC_UTILS. (Not suitable for beginners!) -- Examining closures ------------------------------------------------- Various procedures are available to interrogate closures: To find out how many frozen values there are, use -datalength- datalength(iscolour) => ** 1 -datalist- will list them datalist(identfn(%1, 2, "cat", "dog"%)) => ** [1 2 cat dog] To access or update individual frozen values, the subscriptor -frozval- is provided: frozval(1, iscolour) => ** [red orange yellow green blue indigo violet] We can extend the list of colours thus [pink brown black ] <> frozval(1, iscolour) -> frozval(1, iscolour); iscolour("pink") => ** <true> The procedure used in a closure can be accessed or updated using -pdpart- pdpart(iscolour) => ** <procedure member> -- Closures of traced procedures -------------------------------------- (See HELP * TRACE, for information on traced procedures) If the pdpart procedure is traced after the closure is created, the traced version will not be accessed by the closure: trace member; iscolour("pink") => ** <true> However the traced version can be installed in the closure, using the updater of -pdpart- member -> pdpart(iscolour); iscolour("pink") => > member pink [pink brown black red orange yellow green blue indigo violet] < member <true> ** <true> Similarly, it will have to be re-installed after UNtracing member. -- How closures work -------------------------------------------------- Suppose P is a procedure requiring N arguments. If we create a closure of P with only K arguments (K <= N), e.g. P(Arg1, Arg2, ... ArgK%) then when the closure is run, all that happens is that the K arguments are put on the stack and then P is run. If P requires more than K arguments the remainder should have been placed on the stack previously. For example, if P is a procedure requiring five numbers as arguments, then a closure C with three "frozen values" can be made thus: vars C = P(%3, 4, 5%); Because P has to be run with five arguments, C must be called with two arguments, e.g. C(1, 2); In that case, when C puts 3, 4, and 5 on the stack, 1 and 2 will already be there. Hence P will find and remove five items when it runs. This explains why only the LAST K arguments for a procedure a "frozen" by partial application. -- Notes -------------------------------------------------------------- 1. A closure can itself be partially applied. E.g. C(%2%) is a closure which, when run, will require only one additional argument. 2. A closure can be created with no frozen values, e.g. P(%%) is equivalent to P except that its pdprops, pdnargs, etc can be altered independently of P, and "==" will not recognize them as the same procedure. 3. When a closure of P is run, the call-stack contains no information that P was called from the closure. So, for example -iscaller- will not find the closure, though it will find P. -- See also: ---------------------------------------------------------- For information on procedures that work with CLOSURES see HELP *PARTAPPLY - details of constructing a closure. *PDPART - getting the procedure a closure was built from *PDNARGS - getting or setting the number of arguments required *FROZVAL - getting the values "frozen" into a closure *ISCLOSURE - is something a closure? (Returns 1 if protected) *PDPROPS - a field holding information about a proceudure *UPDATER - updaters and 'selectors' See also HELP *ROUNDBRA - for use of brackets in closure syntax REF * POPSYNTAX - gives the syntax of POP-11 REF * DATA - describes data-types in POP-11 REF * PROCEDURE - describes types of procedures and operations on them. REF * consclosure - describes the basic closure constructor REF * VMCODE - describes some of the underlying mechanisms TEACH * PERCENT - contains simple tutorial information on closures --- C.all/help/closures ------------------------------------------------ --- Copyright University of Sussex 1991. All rights reserved. ----------