1 Metamethods let values that carry a prototype (objects, arrays and 2 resources) customize how the interpreter handles them: a metamethod is a 3 regular function stored under a reserved dunder name on a prototype, invoked 4 automatically when the corresponding operation cannot be completed the normal 5 way. The [manual chapter on prototypes and 6 metamethods](https://ucode-lang.org/manual/) covers the fallback semantics, 7 resolution rules and all edge cases in detail; the examples below collect the 8 common patterns. 9 10 ucode supports five metamethods: 11 12 | Metamethod | Operation customized | 13 |----------------|--------------------------------------------| 14 | `__call__` | calling the value as a function | 15 | `__get__` | reading a property that is not found | 16 | `__set__` | writing a property that is not an own key | 17 | `__delete__` | deleting a property that is not an own key | 18 | `__tostring__` | rendering the value as a string | 19 20 ```ucode 21 let foo = proto({}, { 22 __call__(...args) { return "called with " + args; }, 23 __get__(key) { return key + " is virtual"; }, 24 __set__(key, val) { rawset(this, key, val); }, 25 __delete__(key) { return rawdelete(this, key); }, 26 __tostring__() { return "<my-obj>"; }, 27 }); 28 29 foo(1, 2); // "called with [ 1, 2 ]" 30 foo.missing; // "missing is virtual" 31 foo.other = 42; // routed to __set__ 32 delete foo.ghost; // not an own key, routed to __delete__ 33 print(foo); // <my-obj> 34 ``` 35 36 ## Virtual properties 37 38 `__get__` synthesizes properties that are stored nowhere; returning `null` 39 means "still missing": 40 41 ```ucode 42 let o = proto({}, { 43 __get__(key) { 44 return key == "bar" ? "virtual:bar" : null; 45 } 46 }); 47 48 o.bar; // "virtual:bar" 49 o.foo; // null 50 ``` 51 52 Existing properties, own or inherited, always win over the metamethod. 53 54 ## Delegating to an object 55 56 A `__get__` slot may hold an object instead of a function; a lookup that 57 reaches it re-dispatches on that object with the same key, like Lua's 58 `__index`. This shares a set of defaults without copying them: 59 60 ```ucode 61 let defaults = { host: "0.0.0.0", port: 80 }; 62 let o = proto({}, { __get__: defaults }); 63 64 o.host; // "0.0.0.0" 65 o.port; // 80 66 o.nope; // null, not found there either 67 ``` 68 69 ## Backing stores with raw accessors 70 71 Re-entering the customized operation from inside the metamethod re-dispatches 72 itself into infinite recursion; the escape hatch is `rawget()`, `rawset()` 73 and `rawdelete()`, which perform the underlying access without dispatch. 74 Kept on the instance itself, they give the backing-store pattern: 75 76 ```ucode 77 let o = proto({}, { 78 __get__(key) { 79 let v = rawget(this, "_" + key); 80 return v === null ? "computed:" + key : v; 81 }, 82 __set__(key, val) { 83 rawset(this, "_" + key, val * 10); 84 }, 85 __delete__(key) { 86 return rawdelete(this, "_" + key); 87 } 88 }); 89 90 o.n = 4; 91 o.n; // 40 92 o.other; // "computed:other" 93 delete o.n; // true 94 ``` 95 96 ## Arrays with named fields 97 98 Array indices are raw: a key which is a valid index never dispatches a 99 metamethod. Named keys do, and since arrays have no own-key storage, the 100 values must be routed to other storage, here a plain object captured by 101 closure: 102 103 ```ucode 104 let store = {}; 105 106 let a = proto([ 1, 2 ], { 107 __set__(key, val) { store[key] = val; }, 108 __get__(key) { return store[key]; }, 109 __delete__(key) { return delete store[key]; } 110 }); 111 112 a.tag = "lan"; // __set__ 113 a.tag; // "lan", __get__ 114 delete a.tag; // true, __delete__ 115 a[2]; // null, raw index read, no __get__ 116 ``` 117 118 Without a `__set__`, a non-index write on an array is silently dropped in 119 non-strict mode and raises a type error in strict mode. 120 121 ## Callable values 122 123 `__call__` makes a value invocable; `this` is the value being called, and 124 callable values work anywhere a function is accepted, including as callbacks 125 for builtins: 126 127 ```ucode 128 let add1 = proto({}, { 129 __call__(x) { return x + 1; } 130 }); 131 132 add1(41); // 42 133 map([ 1, 2, 3 ], add1); // [ 2, 3, 4 ] 134 ```
This page was automatically generated by LXR 0.3.1. • OpenWrt