blob: 0672154d59ea500e8398f654a7fd0e5b7e93e6b6 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001CONCEPT
2 closures
3
4NOTE
5 This is the official man page concerning closures. If you find
6 it hard to read maybe the Closure Guide is an easier introduction
7 for you: See closure_guide(LPC) and closures-example(LPC).
8
9DESCRIPTION
10 Closures provide a means of creating code dynamically and
11 passing pieces of code as parameters, storing them in
12 variables. One might think of them as a very advanced form of
13 process_string(). However, this falls short of what you can
14 actually do with them.
15
16 The simplest kind of closures are efuns, lfuns or operators.
17 For example, #'this_player is an example of a closure. You can
18 assign it to a variable as in
19
20 closure f;
21 object p;
22 f = #'this_player;
23
24 and later use either the funcall() or apply() efun to evaluate
25 it. Like
26
27 p = funcall(f);
28
29 or
30
31 p = apply(f);
32
33 In both cases there p will afterwards hold the value of
34 this_player(). Of course, this is only a rather simple
35 application.
36
Zesstra98e90af2021-05-07 19:18:04 +020037 Closure literals can have prefixes to specify which type
38 of closure shall be created:
39
40 #'efun::function_name: closure to an efun
41 #'sefun::function_name: closure to a simul-efun
42 #'lfun::function_name: closure to an lfun
43 #'var::variable_name: closure to a global variable
44
45 Inherited programs can be given as prefixes, too. But they
46 can not be combined with the prefixes above.
47
MG Mud User88f12472016-06-24 23:31:02 +020048 Inline closures are a variant of lfun closures, the difference
49 being that the function text is written right where the
50 closure is used. Since they are pretty powerful by
51 themselves, inline closures have their own manpage.
52
53 More useful instances of closures can be created
54 using the lambda() efun. It is much like the lambda function
55 in LISP. For example, you can do the following:
56
57 f = lambda( ({ 'x }), ({ #'environment, 'x }) );
58
59 This will create a lambda closure and assign it to f. The
60 first argument to lambda is an array describing the arguments
61 (symbols) passed to the closure upon evaluation by funcall()
62 or apply(). You can now evaluate f, for example by means of
63 funcall(f,this_object()). This will result in the following
64 steps:
65
66 1. The value of this_object() will be bound to symbol x.
67 2. environment(x) evaluates to environment(this_object())
68 and is returned as the result of the funcall().
69
70 One might wonder why there are two functions, funcall() and
71 apply(), to perform the seemingly same job, namely evaluating
72 a closure. Of course there is a subtle difference. If the last
73 argument to apply() is an array, then each of its elements
74 gets expanded to an additional paramater. The obvious use
75 would be #'call_other as in:
76
77 mixed eval(object ob,string func,mixed *args) {
78 return apply(#'call_other,ob,func,args);
79 }
80
81 This will result in calling
82 ob->func(args[0],args[1],...,args[sizeof(args)-1]). Using
83 funcall() instead of apply() would have given us
84 ob->func(args).
85
86 Of course, besides efuns there are closures for operators,
87 like #'+, '-, #'<, #'&&, etc.
88
89 Well, so far closures have been pretty much limited despite
90 their obvious flexibility. This changes now with the
91 introduction of conditional and loop operators. For example,
92 try:
93
94 closure max;
95 max = lambda( ({ 'x, 'y }),
96 ({ #'? ,({ #'>, 'x, 'y }), 'x, 'y }) );
97 return funcall(max,7,3);
98
99 The above example will return 7. What happened? Of course #'?
100 is the conditional operator and its 'syntax' is as follows:
101
102 ({ #'?, cond1, val1, cond2, val2, ..., condn, valn,
103 valdefault });
104
105 It evaluates cond1, cond2, ..., condn successively until it
106 gets a nonzero result and then returns the corresponding
107 value. If there is no condition evaluating to a nonzero
108 result, valdefault gets returned. If valdefault is omitted, 0
109 gets returned. #'?! works just like #'?, except that the !
110 operator is applied to conditions before testing. Therefore,
111 while #'? is somewhat like an if statement, #'?! resembles an
112 if_not statement if there were one.
113
114 There are also loops:
115
116 ({ #'do, loopbody1, ..., loopbodyN, loopcond, loopresult })
117
118 will evaluate the loopbodies until loopcond evaluates to 0 and
119 then return the value of loopresult. Symbols may be used as
120 variables, of course.
121
122 ({ #'while, loopcond, loopresult, loopbody1, ..., loopbodyN })
123
124 works similar but evaluates loopcond before the loopbodies.
125
126 The foreach() loop also exists:
127
128 ({ #'foreach, 'var, expr, loopbody1, ..., loopbodyN })
129 ({ #'foreach, ({ 'var1, ..., 'varN}) , expr
130 , loopbody1, ..., loopbodyN })
131
132
133 Now on to a couple of tricky things:
134
135 a) How do I write down an array within a lambda closure to
136 avoid interpretation as a subclosure?
137
138 ({ #'member, ({ "abc", "xyz" }), 'x }) will obviously
139 result in an error as soon as lambda() tries to interpret
140 "abc" as a closure operator. The solution is to quote the
141 array, as in: ({ #'member, '({ "abc", "xyz" }), 'x }).
142 Applying lambda() to this will not result in an error.
143 Instead, the quote will be stripped from the array and the
144 result regarded as a normal array literal. The same can be
145 achieved by using the efun quote(), e.g.:
146
147 ({ #'member, quote( ({ "abc", "xyz" }), 'x ) })
148
149 b) Isn't it a security risk to pass, say, a closure to the
150 master object which then evaluates it with all the
151 permissions it got?
152
153 Luckily, no. Each closure gets upon compilation bound to
154 the object defining it. That means that executing it first
155 sets this_object() to the object that defined it and then
156 evaluates the closure. This also allows us to call lfuns
157 which might otherwise be undefined in the calling object.
158
159 There is however, a variant of lambda(), called
160 unbound_lambda(), which works similar but does not allow
161 the use of lfuns and does not bind the closure to the
162 defining object. The drawback is that trying to evaluate it
163 by apply() or funcall() will result in an error. The
164 closure first needs to be bound by calling bind_lambda().
165 bind_lambda() normally takes one argument and transforms an
166 unbound closure into a closure bound to the object
167 executing the bind_lambda().
168
169 Privileged objects, like the master and the simul_efun
170 object (or those authorized by the privilege_violation()
171 function in the master) may also give an object as the
172 second argument to bind_lambda(). This will bind the
173 closure to that object. A sample application is:
174
175 dump_object(ob)
176 // will dump the variables of ob to /dump.o
177 {
178 closure save;
179 save = unbound_lambda( ({ }),
180 ({ #'save_object, "/open/dump" }) );
181 bind_lambda(save,ob);
182 funcall(save);
183 }
184
185 bind_lambda() can also be used with efun closures.
186
187 c) It might be an interesting application to create closures
188 dynamically as an alternative to writing LPC code to a file
189 and then loading it. However, how do I avoid doing exactly
190 that if I need symbols like 'x or 'y?
191
192 To do that one uses the quote() efun. It takes a string as
193 its argument and transforms it into a symbol. For example,
194 writing quote("x") is exactly the same as writing 'x.
195
196 d) How do I test if a variable holds a closure?
197
198 Use the closurep() efun which works like all the other type
199 testing efuns. For symbols there is also symbolp()
200 available.
201
202 e) That means, I can do:
203 if (closurep(f)) return funcall(f); else return f; ?
204
205 Yes, but in the case of funcall() it is unnecessary. If
206 funcall() gets only one argument and it is not a closure it
207 will be returned unchanged. So return funcall(f); would
208 suffice.
209
210 f) I want to use a function in some object as a closure. How do I do
211 that?
212
213 There are several ways. If the function resides in
214 this_object(), just use #'func_name. If not, or if you want
215 to create the function dnynamically, use the efun
216 symbol_function(). It takes a string as it first and an
217 object as its second argument and returns a closure which
218 upon evaluation calls the given function in the given
219 object (and faster than call_other(), too, if done from
220 inside a loop, since function search will be done only when
221 calling symbol_function().
222
223 g) Can I create efun closures dynamically, too?
224
225 Yes, just use symbol_function() with a single argument.
226 Most useful for marker objects and the like. But
227 theoretically a security risk if not used properly and from
228 inside a security relevant object. Take care, however,
229 that, if there is a simul_efun with the same name, it will
230 be preferred as in the case of #'function. Use the efun::
231 modifier to get the efun if you need it.
232
233 h) Are there other uses of closures except using them to store
234 code?
235
236 Lots. For example, you can use them within almost all of
237 the efuns where you give a function as an argument, like
238 filter(), sort_array() or walk_mapping().
239 sort_array(array,#'>) does indeed what is expected. Another
240 application is set_prompt(), where a closure can output
241 your own prompt based on the current time and other stuff
242 which changes all the time.
243
244 Finally, there are some special efun/operator closures:
245
246 #'[ : indexes an array.
247 #'[< : does the same, but starting at the end.
248 #'[..] : gets an array and two numbers
249 and returns a sub-array.
250 #'[..<] : same as above but the second index is
251 interpreted as counted from the left end.
252 #'[<..] and
253 #'[<..<] : should be clear now.
254 #'[.. : takes only one index and returns the sub-
255 array from this index to the end.
256 #'[<.. : same as above but the index is interpreted
257 as counted from the left end.
258 #'({ : puts all arguments into an array.
259 #'([ : gets an unquoted (!) array which must include
260 at least one element as argument and returns a mapping of
261 the width of the given array's size with one entry that
262 contains the first element as key and the other elements
263 as values to the key.
264
265 #'negate is for unary minus.
266 #', may be followed by any number of closures,
267 e.g.: ({ (#',),
268 ({#'= 'h, 'a, }), ({#'=, 'a, 'b }), ({#'=, 'b, 'h }) })
269 will swap 'a and 'b when compiled and executed.
270
271
272 Procedural elements:
273 ====================
274
275 definition of terms:
276 <block> : zero or more values to be evaluated.
277 <test> : one value to be evaluated as branch or loop condition.
278 <result> : one value to be evaluated at the end of the
279 execution of the form; the value is returned.
280 <lvalue> : local variable/parameter, global variable, or an
281 indexed lvalue.
282 useded EBNF operators:
283 { } iteration
284 [ ] option
285
286 forms:
287 ({#', <body> <result>})
288 ({#'? { <test> <result> } [ <result> ] })
289 ({#'?! { <test> <result> } [ <result> ] })
290 ({#'&& { test } })
291 ({#'|| { test } })
292 ({#'while <test> <result> <body>}) loop while test
293 evaluates non-zero.
294 ({#'do <body> <test> <result>}) loop till test
295 evaluates zero.
296 ({#'= { <lvalue> <value> } }) assignment
297 other assignment
298 operators work, too.
299
300 lisp similars:
301 #', progn
302 #'? cond
303 #'&& and
304 #'|| or
305 #'while do /* but lisp has more syntactic candy here */
306 #'= setq
307
308 A parameter / local variable 'foo' is referenced as 'foo , a
309 global variable as ({#'foo}) . In lvalue positions
310 (assignment), you need not enclose global variable closures in
311 arrays.
312
313 Call by reference parameters are given with ({#'&, <lvalue>})
314
315 Some special efuns:
316 #'[ indexing
317 #'[< indexing from the end
318 #'negate unary -
319
320 Unbound lambda closures
321 =======================
322
323 These closures are not bound to any object. They are created
324 with the efun unbound_lambda() . They cannot contain
325 references to global variables, and all lfun closures are
326 inserted as is, since there is no native object for this
327 closure. You can bind and rebind unbound lambda closures to
328 an object with efun bind_lambda() You need to bind it before
329 it can be called. Ordinary objects can obly bind to
330 themselves, binding to other objects causes a privilege
331 violation(). The point is that previous_object for calls done
332 from inside the closure will reflect the object doing
333 bind_lambda(), and all object / uid based security will also
334 refer to this object.
335
336
337AUTHOR
338 MacBeth, Amylaar, Hyp
339
340SEE ALSO
341 closures-abstract(LPC), closures-example(LPC), closure_guide(LPC)
342 inline-closures(LPC)