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