koto-lang/koto

[question] How to add modules implemented in Koto to the prelude?

jasal82 opened this issue · 7 comments

A previous question was about dynamic loading of modules written in Rust. However, I need something similar for Koto modules. I want my application to download Koto modules based on a dependency specification and make them available to the Koto script executed by the application, preferably without having to import them in the script itself (something like a dynamic prelude). Is that possible somehow?

irh commented

Hi @jasal82, yes that's possible, but with the restriction that your host application and the module libraries must be compiled using the same Rust toolchain so that you avoid potential issues with ABI incompatibilities.

Given that your application is going to be responsible for downloading the modules, then it can also be responsible for building them, and as long as the right toolchain is used then all should be well.

The module libraries can then be dynamically loaded (using libloading or similar), and assuming they expose a make_module function (or something), then the resulting modules can be added to the Koto prelude at runtime.

Let me know if you have any questions, I'll be interested to hear how you get on!

irh commented

@jasal82 Did this answer your question?

Hi @irh, I think you misunderstood the question. It was not about precompiled module libraries. I want to be able to define functions and modules in Koto scripts and add them to the prelude of my interpreter so that other scripts executed on that interpreter can use these functions without having to import anything.

irh commented

I see, I took 'Koto modules' to mean modules for Koto that have been implemented in Rust, which is where the compilation challenges came from.

For modules that are implemented in Koto, the host application would need to:

  1. Compile and run the module script (e.g. using Koto::compile_and_run()).
  2. Place the resulting dependency module's exports map in the prelude (e.g. Koto::prelude().add_map()).

I think this should work? Let me know if you run into any problems.

Sounds straightforward, I'll give it a try.

This works fine:

let mut koto = Koto::new();

let mut koto_core_loader = Koto::new();
let core = fs::read_to_string("core.koto")?;
koto_core_loader.compile_and_run(&core).map_err(koto_error_to_anyhow).context("Error while compiling core")?;
    
let prelude = koto.prelude();
prelude.add_map("core", koto_core_loader.exports().clone());

Now I can extract it into a function and load the Koto scripts specified in my dependency list. Just need to figure out how to handle interdependencies...

irh commented

OK great, let me know if you run into any problems, and I'll be keen to see what you come up with.