blob: 438ba46a87c39fdf3c608bae7d01d38f25a9c046 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001Closure Guide for LPC
2
3Table of Contents
4
5 1 Indroduction, Overview and Efun-Closures
6
7 2 Lfun-, Inline and Lambda-Closures
8 2.1 Lfun-Closures
9 2.2 Inline-Closures
10 2.3 Lambda-Closures
11 2.3.1 Advantages of Lambda-Closures
12 2.3.2 Free Variables in Lambda-Closure Constructs
13 2.3.3 Special Efun-Closures and Operator-Closures for Lambdas
14 2.4 Closures with Strange Names
15 2.5 Operator-Closures
16 2.6 Variable-Closures
17
18 3 Examples
19 3.1 Lfun-Closure
20 3.2 Lambda-Closure
21
221 Introduction, Overview and Efun-Closures
23
24 A closure is a pointer to a function. That means that it is data like an
25 int or a string are. It may be assigned to a variable or given to anoth-
26 er function as argument.
27
28 To create a closure that points to an efun like write() you can write
29 the name of the efun prepended with "hash-tick": #'. #'write is a clo-
30 sure that points to the efun write().
31
32 I very often put parentheses around such a closure-notation because
33 otherwise my editor gets confused by the hashmark: (#'write). This is
34 especially of interest within lambda-closures (see below).
35
36 A closure can be evaluated (which means that the function it points to
37 is called) using the efuns funcall() or apply(), which also allow to
38 give arguments to the function. Example:
39
40 funcall(#'write,"hello");
41
42 This will result in the same as write("hello"); alone. The string
43 "hello" is given as first (and only) argument to the function the clo-
44 sure #'write points to.
45
46 The return value of the function the closure points to is returned by
47 the efun funcall() or apply(). (Since write() always returns 0 the re-
48 turn value of the example above will be 0.)
49
50 What are closures good for? With closures you can make much more univer-
51 sally usable functions. A good example is the function filter().
52 It gets an array and a closure as arguments. Then it calls the function
53 the closure points to for each element of the array:
54
55 filter(({ "bla","foo","bar" }),#'write);
56
57 This will call write("bla"), write("foo") and write("bar") in any order;
58 the order is undefined.
59 (In the current implementation the given closure is evaluated for all
60 elements from the first to the last, so the output will be "blafoobar".)
61
62 Furthermore the efun filter() examines the return value of each
63 call of the function the closure points to (the return value of the
64 write()s). If the value is true (not 0) then this element is put into
65 another array which filter() builds up. If the return value is
66 false (== 0) then this element is _not_ put into this array. When all
67 calls are done the slowly built up array is returned. Thus,
68 filter() filters from the given array all elements that the given
69 closure evaluates "true" for and returns an array of those. (The array
70 given to filter() itself is _not_ changed!)
71
72 A more sensical example for filterwould be this:
73
74 x = filter(users(),#'query_is_wizard);
75
76 users() is an efun that gets no arguments and returns an array of all
77 logged in players (wizards and players). query_is_wizard() is a
78 simul_efun that gets an object as first (and only) argument and returns
79 true (1) if this object is a wizard and 0 otherwise.
80
81 So, for each element of the array returned by users() the function
82 query_is_wizard() is called and only those for which 1 was returned are
83 collected into the result and then put into the variable x.
84
85 We now have all logged in wizards stored as array in the variable x.
86
87 Another example: We want to filter out all numbers that are greater than
88 42 from the array a of integers:
89
90 x = filter(({ 10,50,30,70 }),#'>,42);
91
92 (x will now be ({ 50,70 }).)
93
94 Here two things are new: first: we create a closure that points to an
95 operator; second: we use the possibility to give extra arguments to
96 filter().
97
98 Like all efuns the usual operators can be pointed to with a closure by
99 prepending #' to them. funcall(#'>,4,5) is exactly the same as (4>5).
100
101 The extra arguments given as third to last argument (as many as you
102 like) to filter() are given as second to last argument to the
103 function pointed to by the closure each time it is called.
104
105 Thus we now call (({ 10,50,30,70 })[0]>42), (({ 10,50,30,70 })[1]>42) ...
106 (which is (10>42), (50>42) ...) and return an array of all elements this
107 returns true for and store it into x.
108
109 If you want to create a closure to an efun of which you have the name
110 stored in a string you can create such an efun-closure with the efun
111 symbol_function():
112
113 symbol_function("write") // this will return #'write
114
115 funcall(symbol_function("write"),"foobar"); // == write("foobar");
116
117 This function does not very often occur in normal code but it is very
118 useful for tool-programming (eg the robe uses symbol_function() to allow
119 you call any efun you give).
120
1212 Lfun- and Lambda-Closures
122
123 Very often the possibilities closures to efuns offer are not sufficient
124 for the purpose one has. In nearly all cases three possibilities exist in
125 such cases: use an lfun- or inline-closure, or a lambda-closure.
126
1272.1 Lfun-Closures
128
129 The first possibility is rather easy: like with the efun-closures you
130 can create a pointer to a function in the same object you are by using
131 the #' to prepend it to a function name of a function declared above.
132 Example:
133
134 status foo(int x) {
135 return ((x*2) > 42);
136 }
137
138 int *bar() {
139 return filter(({ 10,50,30,70 }),#'foo);
140 }
141
142 Thus, #'foo is used like there was an efun of this name and doing the
143 job that is done in foo().
144
1452.2 Inline Closure
146
147 Inline closures are a variant of lfun closures, the difference being
148 that the function text is written right where the closure is used,
149 enclosed in a pair of '(:' and ':)'. The compiler will then take care
150 of creating a proper lfun and lfun-closure. The arguments passed to
151 such an inline closure are accessible by position: $1 would be the
152 first argument, $2 the second, and so on. With this, the
153 above example would read:
154
155 int * bar() {
156 return filter(({ 10,50,30,70 }), (: ($1 * 2) > 42 :));
157 }
158
159 or alternatively:
160
161 int * bar() {
162 return filter(({ 10,50,30,70 }), (: return ($1 * 2) > 42; :));
163 }
164
165 The difference between the two versions is that in the first form the text
166 of the inline closure must be an expression only, whereas in the second
167 form any legal statement is allowed. The compiler distinguishes the two
168 forms by the last character before the ':)': if it's a ';' or '}', the
169 compiler treats the closure as statement(s), otherwise as expression.
170
171 Inline closures may also nested, so that the following (not very useful)
172 example is legal, too:
173
174 return filter( ({ 10, 50, 30, 70 })
175 , (: string *s;
176 s = map(users(), (: $1->query_name() :));
177 return s[random(sizeof(s))] + ($1 * 2);
178 :));
179
180 The notation of inline closures is modelled after the MudOS functionals,
181 but there are a few important differences in behaviour.
182
183
1842.3 Lambda-Closures
185
186 Lambda-Closures take the idea of 'define it where you use it' one step
187 further. On first glance they may look like inline closures with an uglier
188 notation, but they offer a few increased possibilities. But first things
189 first.
190
191 The efun lambda() creates a function temporarily and returns a closure
192 pointing to this function. lambda() therefor gets two arrays as
193 arguments, the first is a list of all arguments the function shall expect
194 and the second array is the code of the function (in a more or less
195 complicated form; at least not in C- or LPC-syntax). The closure #'foo
196 from the example above could be notated as lambda-closure:
197
198 lambda(({ 'x }),({ (#'>),
199 ({ (#'*),'x,2 }),
200 42
201 }))
202
203 Now, the first argument is ({ 'x }), an array of all arguments the
204 function shall expect: 1 argument (called 'x) is expected. Notice the
205 strange notation for this argument with one single leading tick. Like
206 The hash-tick to denote closures the leading tick is used to denote
207 things called "symbols". They do not differ much from strings and if
208 you do not want to have a deeper look into closures you can leave it
209 this way.
210
211 The second argument is an array. The first element of such an array
212 must be an efun- or an lfun-closure, the further elements are the
213 arguments for the function this closure points to. If such an argu-
214 ment is an array, it is treated alike; the first element must be a
215 closure and the remaining elements are arguments (which of course
216 also might be arrays ...).
217
218 This leads to a problem: sometimes you want to give an array as an
219 argument to a function. But arrays in an array given to lambda() are
220 interpreted as code-arrays. To allow you to give an array as an argu-
221 ment within an array given to lambda(), you can use the function
222 quote() to make your array to a quoted array (a quoted array is for
223 an array what a symbol is for a string):
224
225 lambda(0,({ (#'sizeof),
226 quote(({ 10,50,30,70 }))
227 }))
228
229 For array constants, you can also use a single quote to the same
230 effect:
231
232 lambda(0,({ (#'sizeof),
233 '({ 10,50,30,70 })
234 }))
235
236 This lambda-closure points to a function that will return 4 (it will
237 call sizeof() for the array ({ 10,50,30,70 })). Another thing: if
238 we want to create a function that expects no arguments, we can give
239 an empty array as first argument to lambda() but we can give 0 as
240 well to attain this. This is just an abbreviation.
241
242 Lambda-closure constructs can become quite large and hard to read. The
243 larger they become the harder the code is to read and you should avoid
244 extreme cases. Very often the possibility to use an lfun or an inline
245 instead of a large lambda shortens the code dramatically. Example:
246
247 status foo(object o) {
248 return environment(o)->query_level()>WL_APPRENTICE;
249 }
250
251 x=filter(a,#'foo);
252
253 does the same as
254
255 x=filter(a,lambda(({ 'o }),
256 ({ (#'>),
257 ({ (#'call_other),
258 ({ (#'environment),'o }),
259 "query_level"
260 }),
261 WL_APPRENTICE
262 })));
263
264 (Note that the syntax with the arrow "->" for call_other()s cannot be
265 used, #'-> does not exist. You have to use #'call_other for this and
266 give the name of the lfun to be called as a string.)
267
268 This example also demonstrates the two disadvantages of lambda closures.
269 First, they are very difficult to read, even for a simple example like
270 this. Second, the lambda closure is re-created everytime the
271 filter() is executed, even though the created code is always the
272 same.
273
274 'Why use lambdas at all then?' you may ask now. Well, read on.
275
276
2772.3.1 Advantages of Lambda Closures
278
279 The advantages of lambdas stem from the fact that they are created
280 at runtime from normal arrays.
281
282 This means that the behaviour of a lambda can be made dependant on data
283 available only at runtime. For example:
284
285 closure c;
286 c = lambda(0, ({#'-, ({ #'time }), time() }) );
287
288 Whenever you now call this closure ('funcall(c)') it will return the
289 elapsed time since the closure was created.
290
291 The second advantage of lambdas is that the arrays from which they
292 are compiled can be constructed at runtime. Imagine a customizable prompt
293 which can be configured to display the time, the environment, or both:
294
295 mixed code;
296
297 code = ({ "> " });
298 if (user_wants_time)
299 code = ({ #'+, ({ #'ctime }), code });
300 if (user_wants_environment)
301 code = ({ #'+, ({#'to_string, ({#'environment, ({#'this_player }) }) })
302 , code });
303 set_prompt(lambda(0, code));
304
305
3062.3.2 Free Variables in Lambda-Closure Constructs
307
308 You can use local variables in lambda constructs without declaring
309 them, just use them. The only limitation is that you at first have
310 to assign something to them. Give them as symbols like you do with
311 the arguments. This feature does not make much sense without the use
312 of complexer flow controlling features described below.
313
314 The closure #'= is used to assign a value to something (like the
315 LPC-operator = is).
316
3172.3.3 Special Efun-Closures and Operator-Closures for Lambdas
318
319 There are some special closures that are supposed to be used only
320 within a lambda construct. With them you can create nearly all code
321 you can with regular LPC-code like loops and conditions.
322
323 #'? acts like the "if" statement in LPC. The first argument is the
324 condition, the second is the code to be executed if the condition
325 returns true. The following arguments can also be such couples of
326 code-arrays that state a condition and a possible result. If at
327 the end there is a single argument, it is used as the else-case
328 if no condition returned true.
329
330 lambda(({ 'x }),({ (#'?), // if
331 ({ (#'>),'x,5 }), // (x > 5)
332 ({ (#'*),'x,2 }), // result is x * 2;
333 ({ (#'<),'x,-5 }), // else if (x < -5)
334 ({ (#'/),'x,2 }), // result is x/2;
335 'x // else result is x;
336 }))
337
338 #'?! is like the #'? but it negates all conditions after evaluation
339 and thus is like an ifnot in LPC (if there were one).
340
341 #', (which looks a bit strange) is the equivalent of the comma-operator
342 in LPC and says: evaluate all arguments and return the value of
343 the last. It is used to do several things inside a lambda-closure.
344
345 lambda(({ 'x }),({ (#',), // two commas necessary!
346 // one for the closure and one as
347 // delimiter in the array
348 ({ (#'write),"hello world!" }),
349 ({ (#'say),"Foobar." })
350 }))
351
352 #'while acts like the LPC statement "while" and repeats executing one
353 code-array while another returns true.
354 #'while expects two or more arguments: the condition as first
355 argument, then the result the whole expression shall have after
356 the condition turns false (this is in many cases of no interest)
357 and as third to last argument the body of the loop.
358
359 lambda(0,({ (#',), // several things to do ...
360 ({ (#'=),'i,0 }), // i is a local variable of this
361 // lambda-closure and is
362 // initialized with 0 now.
363 ({ (#'while),
364 ({ (#'<),'i,10 }), // condition: i < 10
365 42, // result is not interesting,
366 // but we must give one
367 ({ (#'write),'i }), // give out i
368 ({ (#'+=),'i,1 }) // increase i
369 })
370 }))
371
372 The function this closure points to will give out the
373 numbers from 0 to 9 and then return 42.
374
375 #'do is like the do-while statement in LPC and is very much like the
376 #'while. The difference is that #'while tests the condition al-
377 ready before the body is evaluated for the first time, this means
378 that the body might not be evaluated even once. #'do evaluates
379 the body first and then the condition, thus the body is evaluated
380 at least one time.
381 Furthermore, the arguments for #'do are changed in order. #'do
382 expects as first to last but two the body of the loop, then the
383 condition (as last-but-one'th argument) and the result value as
384 last argument. So #'do must have at least two arguments: the
385 condition and the result.
386
387 lambda(0,({ (#',), // several things to do ...
388 ({ (#'=),'i,0 }), // i is a local variable of this
389 // lambda-closure and is initialized
390 // with 0 now.
391 ({ (#'do),
392 ({ (#'write),'i }), // give out i
393 ({ (#'+=),'i,1 }) // increase i
394 ({ (#'<),'i,10 }), // condition: i < 10
395 42 // result is not interesting
396 })
397 }))
398
399 NOTE: There is no #'for in LPC, you should use #'while for this.
400
401 #'foreach is like the foreach() statement in LPC. It evaluates one or
402 more bodies repeatedly for every value in a giving string, array
403 or mapping. The result of the closure is 0.
404
405 #'foreach expects two or more arguments:
406 - a single variable symbol, or an array with several variable
407 symbols
408 - the value to iterate over
409 - zero or more bodes to evaluate in each iteration.
410
411 The single values retrieved from the given value are assigned
412 one after another to the variable(s), then the bodies are executed
413 for each assignment.
414
415 lambda(0, ({#'foreach, 'o, ({#'users})
416 , ({#'call_other, 'o, "die" })
417 }));
418
419 lambda(0, ({#'foreach, ({'k, 'v}), ({ ...mapping...})
420 , ({#'printf, "%O:%O\n", 'k, 'v })
421 }));
422
423 #'return gets one argument and acts like the "return" statement in LPC
424 in the function that is created by lambda(). It aborts the
425 execution of this function and returns the argument.
426
427 lambda(0,({ (#'while),// loop
428 1, // condition is 1 ==> endles loop
429 42, // return value (which will never be used)
430 ({ (#'write),"grin" })
431 ({ (#'?!), // ifnot
432 ({ (#'random),10 }), // (random(10))
433 ({ (#'return),100 }) // return 100;
434 })
435 }))
436
437 This function will enter an endles loop that will in each
438 turn give out "grin" and if random(10) returns 0 (which will
439 of course happen very soon) it will leave the function with
440 "return 100". The value 42 that is given as result of the
441 loop would be returned if the condition would evaluate to 0
442 which cannot be. (1 is never 0 ;-)
443
444 #'break is used like the "break" statement in LPC and aborts the exe-
445 cution of loops and switches.
446 It must not appear outside a loop or switch of the lambda
447 closure itself, it cannot abort the execution of the function
448 the closure points to!
449
450 lambda(0,({ (#'?),
451 ({ (#'random),2 }),
452 ({ (#'break) }), // this will cause the error
453 // "Unimplemented operator break
454 // for lambda()"
455 "random was false!"
456 }));
457
458 You can use ({ #'return,0 }) instead of ({ #'break }) in such
459 cases.
460
461 #'continue is used like the "continue" statement in LPC and jumps to
462 the end of the current loop and continues with the loop
463 condition.
464
465 #'default may be used within a #'switch-construct but be careful!
466 To call symbol_function("default") (which is done usually
467 by tools that allow closure-creation) might crash the
468 driver! So please do only use it within your LPC-files.
469 (NOTE: This driver bug is fixed somewhere below 3.2.1@131.)
470
471 #'.. may be used within a #'switch-construct but is not implemented
472 yet (3.2.1@131). But #'[..] works well instead of it.
473
474 #'switch is used to create closures which behave very much like the
475 switch-construct in LPC. To understand the following you
476 should already know the syntax and possibilities of the
477 latter one (which is mightier than the C-version of switch).
478
479 I will confront some LPC versions and the corresponding clo-
480 sure versions below.
481
482 LPC: Closure:
483 switch (x) { lambda(0,({ (#'switch), x,
484 case 5: ({ 5 }),
485 return "five"; ({ (#'return),"five" }),
486 (#',),
487 case 6..9: ({ 6, (#'[..]), 9 }),
488 return "six to nine"; ({ (#'return),
489 "six to nine" }),
490 (#',),
491 case 1: ({ 1 }),
492 write("one"); ({ (#'write),"one" }),
493 // fall through (#',),
494 case 2: ({ 2,
495 case 10: 10 }),
496 return "two or ten"; ({ (#'return),
497 "two or ten" }),
498 (#',),
499 case 3..4: ({ 3, (#'[..]), 4 }),
500 write("three to four"); ({ (#'write),
501 "three to four" }),
502 break; // leave switch (#'break),
503 default: ({ (#'default) }),
504 write("something else"); ({ (#'write),
505 "something else" }),
506 break; (#'break)
507 } }))
508
509 #'&& evaluates the arguments from the first on and stops if one evalu-
510 ates to 0 and returns 0. If none evaluates to 0 it returns the
511 result of the last argument.
512
513 #'|| evaluates the arguments from the first on and stops if one evalu-
514 ates to true (not 0) and returns it. If all evaluate to 0 it
515 returns 0.
516
517 #'catch executes the closure given as argument, but catches any
518 runtime error (see catch(E)). Optionally the symbols 'nolog,
519 'publish and 'reserve may be given as additional arguments to
520 modify the behaviour of the catch.
521
522 #'sscanf acts similar to how a funcall would, but passes the third
523 and following arguments as lvalues, that is, values which can
524 be assigned to.
525
526 #'= and the #'<op>= variants are also special because the first
527 argument has to be an lvalue.
528
5292.4 Closures with Strange Names
530
531 #'negate is the unary minus that returns -x for the argument x.
532
533 map(({ 1,2,3 }),#'negate)
534
535 This returns ({ -1,-2,-3 }).
536
537 #'[ is used for the things that in LPC are done with
538 the []-operator (it indexes an array or a mapping).
539
540 lambda(0,({ #'[,quote(({ 10,50,30,70 })),2 })) ==> 30
541 lambda(0,({ #'[,([ "x":10;50, "y":30;70 ]),"x",1 })) ==> 50
542
543 #'[< is the same as #'[ but counts the elements from the
544 end (like indexing with [] and the "<").
545
546 #'[..] returns a subarray of the argument from the one
547 given index to the other given index, both counted from the
548 beginning of the array.
549
550 #'[..<]
551 #'[<..]
552 #'[<..<] same as above, but the indexes are counted from the end,
553
554 lambda(0,({ #'[..<],
555 quote(({ 0,1,2,3,4,5,6,7 })),2,3
556 }))
557
558 This will return ({ 2,3,4,5 }).
559 #'[..
560 #'[<.. same as above, but only the first index is given, the
561 subarray will go till the end of the original (like with
562 [x..]).
563
564 #'({ is used to create arrays (as with ({ }) in LPC). All arguments
565 become the elements of the array.
566
567 lambda(0,({ #'({,
568 ({ (#'random),10 }),
569 ({ (#'random),50 }),
570 ({ (#'random),30 }),
571 ({ (#'random),70 })
572 }))
573
574 This returns ({ random(10),random(50),random(30),random(70) }).
575
576 #'([ is used to create mappings out of single entries (with seve-
577 ral values) like the ([ ]) in LPC. Very unusual is the fact
578 that this closure gets arrays as argument that are not eval-
579 uated, although they are not quoted.
580
581 lambda(0,({ #'([,
582 ({ "x",1,2,3 }),
583 ({ "y",4,5,6 })
584 }));
585
586 This returns ([ "x": 1;2;3,
587 "y": 4;5;6 ]).
588
589 However, the elements of the arrays are evaluated as lambda
590 expressions, so if you want to create a mapping from values
591 evaluated at call time, write them as lambda closures:
592
593 lambda(0, ({ #'([, ({ 1, ({ #'ctime }) }) }) )
594
595 will return ([ 1: <result of ctime() at call time ]).
596
597 Arrays can be put into the mapping by quoting:
598
599 lambda(0, ({ #'([, ({ 1, '({ 2 }) }) }) )
600
601 will return ([ 1: ({ 2 }) ])
602
603
604 #'[,] is nearly the same as #'[. The only difference
605 shows up if you want to index a mapping with a width
606 greater than 1 (with more than just one value per
607 key) directly with funcall(). Example:
608 funcall(#'[,([ 0:1;2, 3:4;5 ]),0,1)
609 This will not work. Use #'[,] and it will
610 work. If you want to use it in a lambda closure you
611 do not have to use #'[,] and #'[ will
612 do fine. On the other hand, #'[,] cannot
613 work with arrays, so in nearly all cases use #'[
614 and just in the described special case, use
615 #'[,].
616 This is a strange thing and I deem it a bug, so it
617 might change in the future.
618
6192.5 Operator-Closures
620
621 Most of the closures that are used for things which are done by opera-
622 tors are in fact not operator-closures but efun-closures. But there are
623 a few which do not have the state of efun-closures but are called
624 "operator-closures". #'return is an example, a complete list of them is
625 given below.
626
627 These closures cannot be called directly using funcall() or apply() (or
628 other efuns like filter()), but must appear only in lambda-con-
629 structs.
630
631 funcall(#'return,4); // does not work! This will raise an
632 // Uncallable-closure error.
633 funcall(lambda(0, // this is a correct example
634 ({ (#'return),4 })
635 ));
636
637 All operator-closures:
638 #'&&
639 #'||
640 #',
641 #'?
642 #'?!
643 #'=
644 #'<op>=
645 #'++
646 #'--
647 #'break
648 #'catch
649 #'continue
650 #'default
651 #'do
652 #'foreach
653 #'return
654 #'sscanf
655 #'switch
656 #'while
657 #'({
658 #'([
659
660 #'.. is very likely to be an operator closure too, but since it is
661 not implemented yet, I cannot say for sure.
662
6632.6 Variable-Closures
664
665 All object-global variables might be "closured" by prepending a #' to
666 them to allow access and/or manipulation of them. So if your object has
667 a global variable x you can use #'x within a closure.
668
669 Normally you will treat those expressions like lfun-closures: put them
670 into an array to get the value:
671
672 object.c:
673 int x;
674 int foo() {
675 return lambda(0,({ (#'write),({ (#'x) }) }));
676 }
677
678 Anybody who now calls object->foo() will get a closure which will, when
679 evaluated, write the actual value of object's global variable x.
680
681 Variable closures do not accept arguments.
682
6833 Examples
684
685 In this section I will give and explain some examples coming out of
686 praxis. If the explanation seems to be in some cases too detailed this
687 can be explained by the trial to allow the reader to read the examples
688 section first ;-)
689
6903.1 Lfun-Closure
691
692 An item with a complex long-description like a watch that shall always
693 show the actual time will usually base upon the complex/item-class and
694 give an lfun-closure as argument to the set_long()-method.
695
696 watch.c:
697 inherit "complex/item";
698
699 string my_long() {
700 return ("The watch is small and has a strange otherworldly"
701 " aura about it.\n"
702 "The current time is: "+ctime()+".\n");
703 }
704
705 void create() {
706 set_short("a little watch");
707 set_id(({ "watch","little watch" }));
708 set_long(#'my_long); // the lfun-closure to the lfun my_long()
709 }
710
7113.2 Lambda-Closure
712
713 The example from 3.1 can also be written using a lambda-closure.
714
715 watch.c:
716 inherit "complex/item";
717
718 void create() {
719 set_short("a little watch");
720 set_id(({ "watch","little watch" }));
721 set_long(lambda(0,({ (#'+),
722 "The watch is small and has a strange"
723 " otherworldly aura about it.\n"
724 "The current time is: ",
725 ({ (#'+),
726 ({ (#'ctime) }),
727 ".\n"
728 })
729 })));
730 }