MG Mud User | 88f1247 | 2016-06-24 23:31:02 +0200 | [diff] [blame^] | 1 | CONCEPT |
| 2 | inline closures |
| 3 | |
| 4 | SYNTAX |
| 5 | function <returntype> ( <arguments> ) : <context> { <code> } |
| 6 | |
| 7 | (: <statements> ; <expr>, ... , <expr> :) |
| 8 | |
| 9 | |
| 10 | DESCRIPTION |
| 11 | Inline closures are a way to program closures which are |
| 12 | compiled at the time an object is loaded, but can access |
| 13 | values from their enclosing function at runtime. |
| 14 | |
| 15 | Example: |
| 16 | |
| 17 | closure factory (int arg) { |
| 18 | return function int (int val) { return val * arg; }; |
| 19 | } |
| 20 | |
| 21 | closure f1 = factory(2); |
| 22 | closure f2 = factory(3); |
| 23 | funcall(f1, 3) -> will yield 6. |
| 24 | funcall(f2, 3) -> will yield 9. |
| 25 | |
| 26 | The closure here 'inherits' the current value of the local |
| 27 | variable 'arg' at the time the closure is created. These |
| 28 | values are called the "context" of the closures - they are |
| 29 | stored in a special set of variables in the closure. |
| 30 | |
| 31 | One specific feature of the closure context is that it can be |
| 32 | changed from within the closure, and that these changes remain |
| 33 | permanent: |
| 34 | |
| 35 | closure factory (int arg) { |
| 36 | return function int (int val) { return val * arg++; }; |
| 37 | } |
| 38 | |
| 39 | closure f = factory(2); |
| 40 | funcall(f, 3) -> will yield 6. |
| 41 | funcall(f, 3) -> will now yield 9! |
| 42 | |
| 43 | But changes of the closure context will not reflect on the |
| 44 | local variable it was copied from and vice versa. |
| 45 | |
| 46 | In addition to the implicite context inherited from the |
| 47 | defining function, additional context variables can be defined |
| 48 | in the closure: |
| 49 | |
| 50 | closure factory (int arg) { |
| 51 | return function int (int val) : int x = 2 * arg |
| 52 | { return val * x; }; |
| 53 | } |
| 54 | |
| 55 | closure f = factory(2); |
| 56 | funcall(f, 3) -> will yield 12. |
| 57 | |
| 58 | It is possible to define multiple context variables with and |
| 59 | without initialisation: |
| 60 | |
| 61 | closure factory (int arg) { |
| 62 | return function int (int val) : int y, x = 2 * arg; |
| 63 | int z |
| 64 | { return val * x; }; |
| 65 | } |
| 66 | |
| 67 | These explicite context variables are useful when the closures |
| 68 | needs to keep a state, or to improve performance: |
| 69 | |
| 70 | mapping m = ...; |
| 71 | closure slow (int arg) { |
| 72 | return function mixed () { return m[arg]; } |
| 73 | } |
| 74 | closure fast (int arg) { |
| 75 | return function mixed () : mixed val = m[arg] { return val; } |
| 76 | } |
| 77 | |
| 78 | In the above example, the fast() function executes the lookup |
| 79 | m[arg] only once when the inline closure is created; the |
| 80 | slow() function on the other hand returns a closures which |
| 81 | looks up m[arg] every time it is called. A second effect is |
| 82 | that the results of the slow closure change when m changes; |
| 83 | the result of the fast closure is always the same. |
| 84 | |
| 85 | |
| 86 | In the definition of an inline closure, some elements are |
| 87 | optional: |
| 88 | |
| 89 | <returntype> defaults to 'mixed' |
| 90 | ( <arguments> ) defaults to '(mixed $1 ... mixed $9)' |
| 91 | : <context> no default |
| 92 | |
| 93 | |
| 94 | The special (: :) form is meant for simple expressions (and |
| 95 | MudOS compatibility). The form |
| 96 | |
| 97 | (: <statements> ; <expr>, ..., <expr> :) |
| 98 | |
| 99 | is the shorthand notation for |
| 100 | |
| 101 | function { <statements>; return <expr>, ..., <expr>; } |
| 102 | |
| 103 | For example the two statements |
| 104 | |
| 105 | sort_array(arr, function { return $1 < $2; } ) |
| 106 | sort_array(arr, (: $1 < $2 :) ) |
| 107 | |
| 108 | do the same. The example also demonstrates that both the <statements> |
| 109 | and the <expr> part in this form are optional. |
| 110 | |
| 111 | |
| 112 | |
| 113 | NOTES |
| 114 | The macro __LPC_INLINE_CLOSURES__ is defined when the |
| 115 | inline closures as described here are available. If not |
| 116 | defined, the driver implements a more restricted version |
| 117 | ('(: :)' syntax only, no context variables) for backwards |
| 118 | compatibility. |
| 119 | |
| 120 | Inline closures are not to be confused with inline functions |
| 121 | known from other languages. |
| 122 | |
| 123 | HISTORY |
| 124 | LDMud 3.2.7 implemented the older, restricted form of inline |
| 125 | closures. |
| 126 | LDMud 3.3.271 implemented the full form of inline closures. |
| 127 | LDMud 3.3.275 re-allowed statements in the (: :) form. |
| 128 | |
| 129 | SEE ALSO |
| 130 | closures-abstract(LPC), closures-example(LPC), closure_guide(LPC) |
| 131 | closures(LPC) |