125 lines
5.1 KiB
Markdown
125 lines
5.1 KiB
Markdown
Function Namespaces
|
|
===================
|
|
|
|
{{#include ../links.md}}
|
|
|
|
|
|
Each Function is a Separate Compilation Unit
|
|
--------------------------------------------
|
|
|
|
[Functions] in Rhai are _pure_ and they form individual _compilation units_.
|
|
|
|
This means that individual [functions] can be separated, exported, re-grouped, imported, and
|
|
generally mix-'n-matched with other completely unrelated scripts.
|
|
|
|
For example, the `AST::merge` and `AST::combine` methods (or the equivalent `+` and `+=` operators)
|
|
allow combining all [functions] in one [`AST`] into another, forming a new, unified, group of [functions].
|
|
|
|
|
|
Namespace Types
|
|
---------------
|
|
|
|
In general, there are two main types of _namespaces_ where [functions] are looked up:
|
|
|
|
| Namespace | Quantity | Source | Lookup | Sub-modules? | Variables? |
|
|
| --------- | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | :----------: | :--------: |
|
|
| Global | one | <ol><li>[`AST`] being evaluated</li><li>`Engine::register_XXX` API</li><li>global registered [modules]</li><li>[functions] in [imported][`import`] [modules] marked _global_</li><li>[functions] in registered static [modules] marked _global_</li></ol> | simple name | ignored | ignored |
|
|
| Module | many | <ol><li>[Module] registered via `Engine::register_static_module`</li><li>[Module] loaded via [`import`] statement</li></ol> | namespace-qualified name | yes | yes |
|
|
|
|
### Module Namespaces
|
|
|
|
There can be multiple [module] namespaces at any time during a script evaluation, usually loaded via
|
|
the [`import`] statement.
|
|
|
|
_Static_ [module] namespaces can also be registered into an [`Engine`] via `Engine::register_static_module`.
|
|
|
|
[Functions] and [variables] in module namespaces are isolated and encapsulated within their own environments.
|
|
|
|
They must be called or accessed in a _namespace-qualified_ manner.
|
|
|
|
```js
|
|
import "my_module" as m; // new module namespace 'm' created via 'import'
|
|
|
|
let x = m::calc_result(); // namespace-qualified function call
|
|
|
|
let y = m::MY_NUMBER; // namespace-qualified variable/constant access
|
|
|
|
let z = calc_result(); // <- error: function 'calc_result' not found
|
|
// in global namespace!
|
|
```
|
|
|
|
### Global Namespace
|
|
|
|
There is one _global_ namespace for every [`Engine`], which includes (in the following search order):
|
|
|
|
* all [functions] defined in the [`AST`] currently being evaluated,
|
|
|
|
* all native Rust functions and iterators registered via the `Engine::register_XXX` API,
|
|
|
|
* all functions and iterators defined in global [modules] that are registered into the [`Engine`]
|
|
via `register_global_module`,
|
|
|
|
* functions defined in [modules] registered into the [`Engine`] via `register_static_module` that
|
|
are specifically marked for exposure to the global namespace (e.g. via the `#[rhai(global)]`
|
|
attribute in a [plugin module]).
|
|
|
|
* [functions] defined in [imported][`import`] [modules] that are specifically marked for exposure to
|
|
the global namespace (e.g. via the `#[rhai(global)]` attribute in a [plugin module]).
|
|
|
|
Anywhere in a Rhai script, when a function call is made, the function is searched within the
|
|
global namespace, in the above search order.
|
|
|
|
Therefore, function calls in Rhai are _late_ bound – meaning that the function called cannot be
|
|
determined or guaranteed; there is no way to _lock down_ the function being called.
|
|
This aspect is very similar to JavaScript before ES6 modules.
|
|
|
|
```rust
|
|
// Compile a script into AST
|
|
let ast1 = engine.compile(
|
|
r#"
|
|
fn get_message() {
|
|
"Hello!" // greeting message
|
|
}
|
|
|
|
fn say_hello() {
|
|
print(get_message()); // prints message
|
|
}
|
|
|
|
say_hello();
|
|
"#)?;
|
|
|
|
// Compile another script with an overriding function
|
|
let ast2 = engine.compile(r#"fn get_message() { "Boo!" }"#)?;
|
|
|
|
// Combine the two AST's
|
|
ast1 += ast2; // 'message' will be overwritten
|
|
|
|
engine.run_ast(&ast1)?; // prints 'Boo!'
|
|
```
|
|
|
|
Therefore, care must be taken when _cross-calling_ [functions] to make sure that the correct
|
|
[function] is called.
|
|
|
|
The only practical way to ensure that a [function] is a correct one is to use [modules] –
|
|
i.e. define the [function] in a separate [module] and then [`import`] it:
|
|
|
|
```js
|
|
┌──────────────┐
|
|
│ message.rhai │
|
|
└──────────────┘
|
|
|
|
fn get_message() { "Hello!" }
|
|
|
|
|
|
┌─────────────┐
|
|
│ script.rhai │
|
|
└─────────────┘
|
|
|
|
import "message" as msg;
|
|
|
|
fn say_hello() {
|
|
print(msg::get_message());
|
|
}
|
|
say_hello();
|
|
```
|