| CONCEPT |
| closures |
| |
| NOTE |
| This is the official man page concerning closures. If you find |
| it hard to read maybe the Closure Guide is an easier introduction |
| for you: See closure_guide(LPC) and closures-example(LPC). |
| |
| DESCRIPTION |
| Closures provide a means of creating code dynamically and |
| passing pieces of code as parameters, storing them in |
| variables. One might think of them as a very advanced form of |
| process_string(). However, this falls short of what you can |
| actually do with them. |
| |
| The simplest kind of closures are efuns, lfuns or operators. |
| For example, #'this_player is an example of a closure. You can |
| assign it to a variable as in |
| |
| closure f; |
| object p; |
| f = #'this_player; |
| |
| and later use either the funcall() or apply() efun to evaluate |
| it. Like |
| |
| p = funcall(f); |
| |
| or |
| |
| p = apply(f); |
| |
| In both cases there p will afterwards hold the value of |
| this_player(). Of course, this is only a rather simple |
| application. |
| |
| Inline closures are a variant of lfun closures, the difference |
| being that the function text is written right where the |
| closure is used. Since they are pretty powerful by |
| themselves, inline closures have their own manpage. |
| |
| More useful instances of closures can be created |
| using the lambda() efun. It is much like the lambda function |
| in LISP. For example, you can do the following: |
| |
| f = lambda( ({ 'x }), ({ #'environment, 'x }) ); |
| |
| This will create a lambda closure and assign it to f. The |
| first argument to lambda is an array describing the arguments |
| (symbols) passed to the closure upon evaluation by funcall() |
| or apply(). You can now evaluate f, for example by means of |
| funcall(f,this_object()). This will result in the following |
| steps: |
| |
| 1. The value of this_object() will be bound to symbol x. |
| 2. environment(x) evaluates to environment(this_object()) |
| and is returned as the result of the funcall(). |
| |
| One might wonder why there are two functions, funcall() and |
| apply(), to perform the seemingly same job, namely evaluating |
| a closure. Of course there is a subtle difference. If the last |
| argument to apply() is an array, then each of its elements |
| gets expanded to an additional paramater. The obvious use |
| would be #'call_other as in: |
| |
| mixed eval(object ob,string func,mixed *args) { |
| return apply(#'call_other,ob,func,args); |
| } |
| |
| This will result in calling |
| ob->func(args[0],args[1],...,args[sizeof(args)-1]). Using |
| funcall() instead of apply() would have given us |
| ob->func(args). |
| |
| Of course, besides efuns there are closures for operators, |
| like #'+, '-, #'<, #'&&, etc. |
| |
| Well, so far closures have been pretty much limited despite |
| their obvious flexibility. This changes now with the |
| introduction of conditional and loop operators. For example, |
| try: |
| |
| closure max; |
| max = lambda( ({ 'x, 'y }), |
| ({ #'? ,({ #'>, 'x, 'y }), 'x, 'y }) ); |
| return funcall(max,7,3); |
| |
| The above example will return 7. What happened? Of course #'? |
| is the conditional operator and its 'syntax' is as follows: |
| |
| ({ #'?, cond1, val1, cond2, val2, ..., condn, valn, |
| valdefault }); |
| |
| It evaluates cond1, cond2, ..., condn successively until it |
| gets a nonzero result and then returns the corresponding |
| value. If there is no condition evaluating to a nonzero |
| result, valdefault gets returned. If valdefault is omitted, 0 |
| gets returned. #'?! works just like #'?, except that the ! |
| operator is applied to conditions before testing. Therefore, |
| while #'? is somewhat like an if statement, #'?! resembles an |
| if_not statement if there were one. |
| |
| There are also loops: |
| |
| ({ #'do, loopbody1, ..., loopbodyN, loopcond, loopresult }) |
| |
| will evaluate the loopbodies until loopcond evaluates to 0 and |
| then return the value of loopresult. Symbols may be used as |
| variables, of course. |
| |
| ({ #'while, loopcond, loopresult, loopbody1, ..., loopbodyN }) |
| |
| works similar but evaluates loopcond before the loopbodies. |
| |
| The foreach() loop also exists: |
| |
| ({ #'foreach, 'var, expr, loopbody1, ..., loopbodyN }) |
| ({ #'foreach, ({ 'var1, ..., 'varN}) , expr |
| , loopbody1, ..., loopbodyN }) |
| |
| |
| Now on to a couple of tricky things: |
| |
| a) How do I write down an array within a lambda closure to |
| avoid interpretation as a subclosure? |
| |
| ({ #'member, ({ "abc", "xyz" }), 'x }) will obviously |
| result in an error as soon as lambda() tries to interpret |
| "abc" as a closure operator. The solution is to quote the |
| array, as in: ({ #'member, '({ "abc", "xyz" }), 'x }). |
| Applying lambda() to this will not result in an error. |
| Instead, the quote will be stripped from the array and the |
| result regarded as a normal array literal. The same can be |
| achieved by using the efun quote(), e.g.: |
| |
| ({ #'member, quote( ({ "abc", "xyz" }), 'x ) }) |
| |
| b) Isn't it a security risk to pass, say, a closure to the |
| master object which then evaluates it with all the |
| permissions it got? |
| |
| Luckily, no. Each closure gets upon compilation bound to |
| the object defining it. That means that executing it first |
| sets this_object() to the object that defined it and then |
| evaluates the closure. This also allows us to call lfuns |
| which might otherwise be undefined in the calling object. |
| |
| There is however, a variant of lambda(), called |
| unbound_lambda(), which works similar but does not allow |
| the use of lfuns and does not bind the closure to the |
| defining object. The drawback is that trying to evaluate it |
| by apply() or funcall() will result in an error. The |
| closure first needs to be bound by calling bind_lambda(). |
| bind_lambda() normally takes one argument and transforms an |
| unbound closure into a closure bound to the object |
| executing the bind_lambda(). |
| |
| Privileged objects, like the master and the simul_efun |
| object (or those authorized by the privilege_violation() |
| function in the master) may also give an object as the |
| second argument to bind_lambda(). This will bind the |
| closure to that object. A sample application is: |
| |
| dump_object(ob) |
| // will dump the variables of ob to /dump.o |
| { |
| closure save; |
| save = unbound_lambda( ({ }), |
| ({ #'save_object, "/open/dump" }) ); |
| bind_lambda(save,ob); |
| funcall(save); |
| } |
| |
| bind_lambda() can also be used with efun closures. |
| |
| c) It might be an interesting application to create closures |
| dynamically as an alternative to writing LPC code to a file |
| and then loading it. However, how do I avoid doing exactly |
| that if I need symbols like 'x or 'y? |
| |
| To do that one uses the quote() efun. It takes a string as |
| its argument and transforms it into a symbol. For example, |
| writing quote("x") is exactly the same as writing 'x. |
| |
| d) How do I test if a variable holds a closure? |
| |
| Use the closurep() efun which works like all the other type |
| testing efuns. For symbols there is also symbolp() |
| available. |
| |
| e) That means, I can do: |
| if (closurep(f)) return funcall(f); else return f; ? |
| |
| Yes, but in the case of funcall() it is unnecessary. If |
| funcall() gets only one argument and it is not a closure it |
| will be returned unchanged. So return funcall(f); would |
| suffice. |
| |
| f) I want to use a function in some object as a closure. How do I do |
| that? |
| |
| There are several ways. If the function resides in |
| this_object(), just use #'func_name. If not, or if you want |
| to create the function dnynamically, use the efun |
| symbol_function(). It takes a string as it first and an |
| object as its second argument and returns a closure which |
| upon evaluation calls the given function in the given |
| object (and faster than call_other(), too, if done from |
| inside a loop, since function search will be done only when |
| calling symbol_function(). |
| |
| g) Can I create efun closures dynamically, too? |
| |
| Yes, just use symbol_function() with a single argument. |
| Most useful for marker objects and the like. But |
| theoretically a security risk if not used properly and from |
| inside a security relevant object. Take care, however, |
| that, if there is a simul_efun with the same name, it will |
| be preferred as in the case of #'function. Use the efun:: |
| modifier to get the efun if you need it. |
| |
| h) Are there other uses of closures except using them to store |
| code? |
| |
| Lots. For example, you can use them within almost all of |
| the efuns where you give a function as an argument, like |
| filter(), sort_array() or walk_mapping(). |
| sort_array(array,#'>) does indeed what is expected. Another |
| application is set_prompt(), where a closure can output |
| your own prompt based on the current time and other stuff |
| which changes all the time. |
| |
| Finally, there are some special efun/operator closures: |
| |
| #'[ : indexes an array. |
| #'[< : does the same, but starting at the end. |
| #'[..] : gets an array and two numbers |
| and returns a sub-array. |
| #'[..<] : same as above but the second index is |
| interpreted as counted from the left end. |
| #'[<..] and |
| #'[<..<] : should be clear now. |
| #'[.. : takes only one index and returns the sub- |
| array from this index to the end. |
| #'[<.. : same as above but the index is interpreted |
| as counted from the left end. |
| #'({ : puts all arguments into an array. |
| #'([ : gets an unquoted (!) array which must include |
| at least one element as argument and returns a mapping of |
| the width of the given array's size with one entry that |
| contains the first element as key and the other elements |
| as values to the key. |
| |
| #'negate is for unary minus. |
| #', may be followed by any number of closures, |
| e.g.: ({ (#',), |
| ({#'= 'h, 'a, }), ({#'=, 'a, 'b }), ({#'=, 'b, 'h }) }) |
| will swap 'a and 'b when compiled and executed. |
| |
| |
| Procedural elements: |
| ==================== |
| |
| definition of terms: |
| <block> : zero or more values to be evaluated. |
| <test> : one value to be evaluated as branch or loop condition. |
| <result> : one value to be evaluated at the end of the |
| execution of the form; the value is returned. |
| <lvalue> : local variable/parameter, global variable, or an |
| indexed lvalue. |
| useded EBNF operators: |
| { } iteration |
| [ ] option |
| |
| forms: |
| ({#', <body> <result>}) |
| ({#'? { <test> <result> } [ <result> ] }) |
| ({#'?! { <test> <result> } [ <result> ] }) |
| ({#'&& { test } }) |
| ({#'|| { test } }) |
| ({#'while <test> <result> <body>}) loop while test |
| evaluates non-zero. |
| ({#'do <body> <test> <result>}) loop till test |
| evaluates zero. |
| ({#'= { <lvalue> <value> } }) assignment |
| other assignment |
| operators work, too. |
| |
| lisp similars: |
| #', progn |
| #'? cond |
| #'&& and |
| #'|| or |
| #'while do /* but lisp has more syntactic candy here */ |
| #'= setq |
| |
| A parameter / local variable 'foo' is referenced as 'foo , a |
| global variable as ({#'foo}) . In lvalue positions |
| (assignment), you need not enclose global variable closures in |
| arrays. |
| |
| Call by reference parameters are given with ({#'&, <lvalue>}) |
| |
| Some special efuns: |
| #'[ indexing |
| #'[< indexing from the end |
| #'negate unary - |
| |
| Unbound lambda closures |
| ======================= |
| |
| These closures are not bound to any object. They are created |
| with the efun unbound_lambda() . They cannot contain |
| references to global variables, and all lfun closures are |
| inserted as is, since there is no native object for this |
| closure. You can bind and rebind unbound lambda closures to |
| an object with efun bind_lambda() You need to bind it before |
| it can be called. Ordinary objects can obly bind to |
| themselves, binding to other objects causes a privilege |
| violation(). The point is that previous_object for calls done |
| from inside the closure will reflect the object doing |
| bind_lambda(), and all object / uid based security will also |
| refer to this object. |
| |
| |
| AUTHOR |
| MacBeth, Amylaar, Hyp |
| |
| SEE ALSO |
| closures-abstract(LPC), closures-example(LPC), closure_guide(LPC) |
| inline-closures(LPC) |