Implement capturing.

This commit is contained in:
Stephen Chung
2020-07-30 18:18:28 +08:00
parent e505a06839
commit 98b294c699
17 changed files with 410 additions and 180 deletions

View File

@@ -100,21 +100,22 @@ The Rhai Scripting Language
8. [Maximum Call Stack Depth](safety/max-call-stack.md)
9. [Maximum Statement Depth](safety/max-stmt-depth.md)
7. [Advanced Topics](advanced.md)
1. [Object-Oriented Programming (OOP)](language/oop.md)
2. [Serialization/Deserialization of `Dynamic` with `serde`](rust/serde.md)
3. [Script Optimization](engine/optimize/index.md)
1. [Capture Scope for Function Call](language/fn-capture.md)
2. [Object-Oriented Programming (OOP)](language/oop.md)
3. [Serialization/Deserialization of `Dynamic` with `serde`](rust/serde.md)
4. [Script Optimization](engine/optimize/index.md)
1. [Optimization Levels](engine/optimize/optimize-levels.md)
2. [Re-Optimize an AST](engine/optimize/reoptimize.md)
3. [Eager Function Evaluation](engine/optimize/eager.md)
4. [Side-Effect Considerations](engine/optimize/side-effects.md)
5. [Volatility Considerations](engine/optimize/volatility.md)
6. [Subtle Semantic Changes](engine/optimize/semantics.md)
4. [Low-Level API](rust/register-raw.md)
5. [Use as DSL](engine/dsl.md)
5. [Low-Level API](rust/register-raw.md)
6. [Use as DSL](engine/dsl.md)
1. [Disable Keywords and/or Operators](engine/disable.md)
2. [Custom Operators](engine/custom-op.md)
3. [Extending with Custom Syntax](engine/custom-syntax.md)
6. [Eval Statement](language/eval.md)
7. [Eval Statement](language/eval.md)
8. [Appendix](appendix/index.md)
1. [Keywords](appendix/keywords.md)
2. [Operators and Symbols](appendix/operators.md)

View File

@@ -5,6 +5,8 @@ Advanced Topics
This section covers advanced features such as:
* [Capture the calling scope]({{rootUrl}}/language/fn-capture.md) in a function call.
* Simulated [Object Oriented Programming (OOP)][OOP].
* [`serde`] integration.

View File

@@ -54,7 +54,7 @@ WARNING - NOT Closures
----------------------
Remember: anonymous functions, though having the same syntax as Rust _closures_, are themselves
**not** closures. In particular, they do not capture their running environment. They are more like
**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`]

View File

@@ -0,0 +1,62 @@
Capture The Calling Scope for Function Call
==========================================
{{#include ../links.md}}
Peeking Out of The Pure Box
---------------------------
Rhai functions are _pure_, meaning that they depend on on their arguments and have no
access to the calling environment.
When a function accesses a variable that is not defined within that function's scope,
it raises an evaluation error.
It is possible, through a special syntax, to capture the calling scope - i.e. the scope
that makes the function call - and access variables defined there.
```rust
fn foo(y) { // function accesses 'x' and 'y', but 'x' is not defined
x += y; // 'x' is modified in this function
x
}
let x = 1;
foo(41); // error: variable 'x' not found
// Calling a function with a '!' causes it to capture the calling scope
foo!(41) == 42; // the function can access the value of 'x', but cannot change it
x == 1; // 'x' is still the original value
x.method!(); // <- syntax error: capturing is not allowed in method-call style
// Capturing also works for function pointers
let f = Fn("foo");
call!(f, 41) == 42; // must use function-call style
f.call!(41); // <- syntax error: capturing is not allowed in method-call style
```
No Mutations
------------
Variables in the calling scope are accessed as copies.
Changes to them do not reflect back to the calling scope.
Rhai functions remain _pure_ in the sense that they can never mutate their environment.
Caveat Emptor
-------------
Functions relying on the calling scope is a _Very Bad Idea™_ because it makes code almost impossible
to reason and maintain, as their behaviors are volatile and unpredictable.
This usage should be at the last resort.

View File

@@ -1,6 +1,8 @@
Capture External Variables via Automatic Currying
================================================
{{#include ../links.md}}
Poor Man's Closures
-------------------

View File

@@ -33,7 +33,7 @@ curried.call(2) == 42; // <- de-sugars to 'func.call(21, 2)'
Automatic Currying
------------------
[Anonymous functions] defined via a closure syntax _capture_ external variables that are not shadowed inside
the function's scope.
[Anonymous functions] defined via a closure syntax _capture_ the _values_ of external variables
that are not shadowed inside the function's scope.
This is accomplished via [automatic currying].

View File

@@ -23,7 +23,7 @@ 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 external variables in [anonymous functions]. |
| `no_capture` | Disable [capturing][capture] external variables in [anonymous functions] and [capturing the calling scope]({{rootUrl}}/language/fn-capture.md) in function calls. |
| `no_std` | Build for `no-std`. 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. |