• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ucode/docs/tutorials/04-metamethods.md

  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