wren-lang / wren

The Wren Programming Language. Wren is a small, fast, class-based concurrent scripting language.
http://wren.io
MIT License
6.9k stars 552 forks source link

Simpler bindings? #1064

Closed brjoha closed 2 years ago

brjoha commented 2 years ago

Looking at the documentation, would it not be easier to let the host application push its bindings directly like this?

// given these static functions
static void add(WrenVM* vm);  // expects two Num and returns Num
static void sub(WrenVM* vm);  // expects two Num and returns Num
static void odd(WrenVM* vm);  // expects one Num and returns Bool
static void even(WrenVM* vm);  // expects one Num and returns Bool

// export into wren like this
bindStaticFn(vm, "main", "Math", "Num add(Num, Num)", Math::add);
bindStaticFn(vm, "main", "Math", "Num sub(Num, Num)", Math::sub);
bindStaticFn(vm, "main", "Math", "Bool odd(Num)",     Math::odd);
bindStaticFn(vm, "main", "Math", "Bool even(Num)",    Math::even);

It's far less tedious IMO.

Similarly, an API like this could be used to export a class into wren...

bindClass(vm, "main", "File", "create(String)", File::allocate, File::finalize);
bindClassMethod(vm, "main", "File", "write(String)", File::write);
bindClassMethod(vm, "main", "File", "close()", File::close);

The current mechanisms for embedding are really holding me back from adopting wren :( I want something simple so that changes on the application side are easily reflected on the wren side.

If type safety were optional, more work could be handed to wren by having it deal with slots. For example, application binds these functions instead...

static double add(double lhs, double rhs);
static double sub(double lhs, double rhs);
static bool odd(double val);
static bool even(double val);
mhermier commented 2 years ago

Both API are functional.

The one that you propose has a slight disadvantage that it requires that the user actively push entries before they are solved, requiring 2 lookup instead of one.

Current one is solved on demand, which is more flexible, since it can result in lazy evaluation and provide JIT features of that is a thing in the future.

But I agree the API you propose is a little bit more pleasant to the eyes. Maybe we should provide an intermediate solution like the one that is present in wren-cli (using macro to automatically build lookup tables).

joshgoebel commented 2 years ago

would it not be easier to let the host application push its bindings directly like this?

Couldn't that higher-level API be built easily on top of what we already have (which is quite low-level [I presume on purpose])? If so then the question is should it really be in core if anyone can easily add it outside core as a layer on top.

ruby0x1 commented 2 years ago

It's one of the cases where there's several approaches that fit several use cases, having one specific method baked in isn't as ideal. The VM needs to provides tools that can be shaped to fit the use case at hand, which it does. An example being the multiple language bindings that are around, probably can't use the macro version because of string literals, which makes it a little too specific.

Having an easy to drop in wrapper in a single header or something as a separate helper that people can use is a good idea though, since likely there will be multiple.

Personally, I found the binding to be simple enough to not need any fixing, and the higher level workflows that are more specific can exist higher up.

joshgoebel commented 2 years ago

@brjoha You might take a look at the work I've done to auto-generate the C headers for the my console project: https://github.com/joshgoebel/wren-console/pull/12/files

IE, a Wren script analyzes all the Wren code and auto-rebuilds all the C bindings... then the C functions just have to be written... so adding new functionality is as simple as adding the Wren code, regen the headers, then jump in an add the necessary C code... with more work I'm sure you could have it write empty C functions in proper files as well if your project were organized well enough...

jagoly commented 2 years ago

You might be interested in one of the C++ bindings that have been created for wren.

The oldest (but still good and usable) of these is wrenpp. It's fairly complete and I used it for a while.

Another that looks to be quite interesting is wrenbind17. I don't use it since it seems to be a bit too high level for my tastes, but that may be what you're after.

Finally there is my own binding, which I've dubbed WrenPlus. It's similar to wrenpp but supports catching C++ exceptions in foreign methods and converting them to wren errors. It's not feature complete since I'm only adding things to it as I specifically need them for my own project. Also, currently the only documentation is the comments in the headers/source. I do plan to move it to its own repo and write a readme for it at some point.

My main point I suppose is that wren's barebones binding api is actually a feature, you can write higher level wrappers for it quite nicely.