rhaiscript / rhai

Rhai - An embedded scripting language for Rust.
https://crates.io/crates/rhai
Apache License 2.0
3.79k stars 177 forks source link

Script error cross-calling functions within modules #170

Closed elasmojs closed 4 years ago

elasmojs commented 4 years ago

Hi

The attached module scripts gives error; function not found when calling a module function from within the same module. moduletest.zip

Am I doing anything wrong?

Thanks

schungx commented 4 years ago

You're right. It is a bug.

The engine is searching for the function in the global scope, not within the modules.

Seems like right now module functions cannot refer to each other...

This may need some time to fix.

schungx commented 4 years ago

@elasmojs turns out it may not be trivial to resolve this bug, because functions in a module tree is flattened and loses its structure. That an implication of the particular design of Rhai. Modules are not self-contained spaces like many mainstream languages. Think of it as more like JavaScript modules before ES6.

If you depend on this feature for your project, maybe you need to try another library for now...

elasmojs commented 4 years ago

I am glad to have helped identify this. It is not a big impediment for my project, Rhai is a wonderful fit

schungx commented 4 years ago

Let's keep this on until we fix the bug...

schungx commented 4 years ago

Just for future references, this is why functions are problematic in Rhai:

The design of Rhai is peculiar in that functions defined in a script are handled separately from the main script. Those functions can be collected, taken apart, and re-grouped. They can be shared and merged into another script. In other words, each function is really a separate compilation unit - think of each function as a separate module or script file.

A Rhai script, when performing a function call, looks up the function by name at runtime. That really depends on what function under that name is loaded at that particular time because, as I discussed above, functions from another script can be easily merged into the current script, augmenting or changing behavior.

Because of this, a Rhai function really cannot refer to anything beyond its boundaries. They are all pure. This design has some real benefits:

In other words, Rhai functions are very similar to JavaScript functions, with the exception that there is even no global scope and no scope chain.

Therefore, when a function calls another, it really doesn't know what function it is calling, because the user can load different functions at runtime under that name. Therefore, in order for a Rhai function to call another, you must make sure that the function is the right one, because essentially each function is a separate compilation unit.

For example:

fn perform_action() {
    ...
}
fn do_something() {
    perform_action();
}

When this do_something is called, perform_action is looked up at runtime. It may not be the same perform_action defined in the same script - that function may not even be there any more, if do_something is imported to another script by itself. Essentially, perform_action and do_something in the same script have no fixed relationship with each other.

To actually make this work, you'll need to actually have two modules:

action.rhai:

fn perform_action() { ... }

work.rhai:

fn do_something() {
    import "action" as act;      // make sure we load the correct compilation unit
    act::perform_action();        // and call the right function
}

This consideration - that all functions are detached and resolved at runtime, is a characteristic of Rhai and thus makes it different from most other mainstream scripting languages.

schungx commented 4 years ago

Closing this for now, as unlikely to be fixed in the near future due to the particular concept of separating functions from the code body.