Implement closures.

This commit is contained in:
Stephen Chung
2020-08-03 12:10:20 +08:00
parent 747c0345f2
commit 4079164bfd
24 changed files with 340 additions and 588 deletions

View File

@@ -10,8 +10,6 @@ Keywords List
| `let` | Variable declaration | | No |
| `const` | Constant declaration | | No |
| `is_shared` | Is a value shared? | | No |
| `shared` | Share value | [`no_shared`] | No |
| `take` | Un-share value | [`no_shared`] | No |
| `if` | If statement | | No |
| `else` | else block of if statement | | No |
| `while` | While loop | | No |
@@ -44,6 +42,7 @@ Reserved Keywords
| --------- | --------------------- |
| `var` | Variable declaration |
| `static` | Variable declaration |
| `shared` | Share value |
| `do` | Looping |
| `each` | Looping |
| `then` | Control flow |

View File

@@ -22,7 +22,7 @@ fn print_obj() { print(this.data); }
```
The above can be replaced by using _anonymous functions_ which have the same syntax as Rust's closures
(but they are **NOT** closures, merely syntactic sugar):
(but they are **NOT** real closures, merely syntactic sugar):
```rust
let obj = #{
@@ -50,12 +50,10 @@ fn anon_fn_1002() { print this.data; }
```
WARNING - NOT Closures
----------------------
WARNING - NOT Real Closures
--------------------------
Remember: anonymous functions, though having the same syntax as Rust _closures_, are themselves
**not** closures. In particular, they do not capture their execution environment. They are more like
Rust's function pointers.
They do, however, _capture_ variable _values_ from their execution environment, unless the [`no_capture`]
feature is turned on. This is accomplished via [automatic currying][capture].
**not** real closures.
In particular, they capture their execution environment via [automatic currying][capture],
unless the [`no_closure`] feature is turned on.

View File

@@ -15,7 +15,7 @@ is created.
Variables that are accessible during the time the [anonymous function] is created can be captured,
as long as they are not shadowed by local variables defined within the function's scope.
The values captured are the values of those variables at the time of the [anonymous function]'s creation.
The captured variables are automatically converted into reference-counted shared values.
New Parameters For Captured Variables
@@ -29,28 +29,32 @@ In actual implementation, this de-sugars to:
3. The variable is added to the parameters list of the anonymous function, at the front.
4. The current value of the variable is then [curried][currying] into the [function pointer] itself, essentially carrying that value and inserting it into future calls of the function.
4. The variable is then turned into a reference-counted shared value.
Automatic currying can be turned off via the [`no_capture`] feature.
5. The shared value is then [curried][currying] into the [function pointer] itself, essentially carrying a reference to that shared value and inserting it into future calls of the function.
Automatic currying can be turned off via the [`no_closure`] feature.
Examples
--------
```rust
let x = 40;
let x = 1;
let f = |y| x + y; // current value of variable 'x' is auto-curried
// the value 40 is curried into 'f'
let f = |y| x + y; // variable 'x' is auto-curried (captured) into 'f'
// 'x' is converted into a shared value
x = 1; // 'x' can be changed but the curried value is not
x = 40; // 'x' can be changed
f.call(2) == 42; // the value of 'x' is still 40
f.call(2) == 42; // the value of 'x' is 40 because 'x' is shared
// The above de-sugars into this:
fn anon$1001(x, y) { x + y } // parameter 'x' is inserted
let f = Fn("anon$1001").curry(x); // current value of 'x' is curried
make_shared(x); // convert 'x' into a shared value
let f = Fn("anon$1001").curry(x); // shared 'x' is curried
f.call(2) == 42;
```

View File

@@ -9,7 +9,7 @@ The following are reserved keywords in Rhai:
| ------------------------------------------------- | ------------------------------------------------ | --------------------- | :--------------------: |
| `true`, `false` | | Boolean constants | |
| `let`, `const` | `var`, `static` | Variable declarations | |
| `shared`, `take`, `is_shared` | | Shared values | [`no_shared`] |
| `is_shared` | | Shared values | [`no_closure`] |
| `if`, `else` | `then`, `goto`, `exit` | Control flow | |
| | `switch`, `match`, `case` | Matching | |
| `while`, `loop`, `for`, `in`, `continue`, `break` | `do`, `each` | Looping | |

View File

@@ -30,26 +30,33 @@ that resembles very closely that of class methods in an OOP language.
Anonymous functions can also _capture_ variables from the defining environment, which is a very
common OOP pattern. Capturing is accomplished via a feature called _[automatic currying]_ and
can be turned off via the [`no_capture`] feature.
can be turned off via the [`no_closure`] feature.
Examples
--------
```rust
let factor = 1;
// Define the object
let obj =
#{
data: 0,
increment: |x| this.data += x, // when called, 'this' binds to 'obj'
update: |x| this.data = x, // when called, 'this' binds to 'obj'
action: || print(this.data) // when called, 'this' binds to 'obj'
increment: |x| this.data += x, // 'this' binds to 'obj'
update: |x| this.data = x * factor, // 'this' binds to 'obj', 'factor' is captured
action: || print(this.data) // 'this' binds to 'obj'
};
// Use the object
obj.increment(1);
obj.action(); // prints 1
obj.action(); // prints 1
obj.update(42);
obj.action(); // prints 42
obj.action(); // prints 42
factor = 2;
obj.update(42);
obj.action(); // prints 84
```

View File

@@ -9,8 +9,7 @@
[`no_object`]: {{rootUrl}}/start/features.md
[`no_function`]: {{rootUrl}}/start/features.md
[`no_module`]: {{rootUrl}}/start/features.md
[`no_capture`]: {{rootUrl}}/start/features.md
[`no_shared`]: {{rootUrl}}/start/features.md
[`no_closure`]: {{rootUrl}}/start/features.md
[`no_std`]: {{rootUrl}}/start/features.md
[`no-std`]: {{rootUrl}}/start/features.md
[`internals`]: {{rootUrl}}/start/features.md

View File

@@ -23,9 +23,8 @@ more control over what a script can (or cannot) do.
| `no_object` | Disable support for [custom types] and [object maps]. |
| `no_function` | Disable script-defined [functions]. |
| `no_module` | Disable loading external [modules]. |
| `no_capture` | Disable [capturing][capture] external variables in [anonymous functions] and [capturing the calling scope]({{rootUrl}}/language/fn-capture.md) in function calls. |
| `no_shared` | Disable sharing of data values. |
| `no_std` | Build for `no-std` (implies `no_shared`). Notice that additional dependencies will be pulled in to replace `std` features. |
| `no_closure` | Disable [capturing][capture] external variables in [anonymous functions] to simulate _closures_, or [capturing the calling scope]({{rootUrl}}/language/fn-capture.md) in function calls. |
| `no_std` | Build for `no-std` (implies `no_closure`). Notice that additional dependencies will be pulled in to replace `std` features. |
| `serde` | Enable serialization/deserialization via `serde`. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. |
| `internals` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. |
| `unicode-xid-ident` | Allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) as identifiers. |