Stream: general

Topic: ✔ functions taking functions as arguments in wit


view this post on Zulip code red art (Nov 17 2023 at 12:01):

According to the wit spec, it seems a function can only take parameters of these types

ty ::= 'u8' | 'u16' | 'u32' | 'u64'
     | 's8' | 's16' | 's32' | 's64'
     | 'float32' | 'float64'
     | 'char'
     | 'bool'
     | 'string'
     | tuple
     | list
     | option
     | result
     | handle
     | id

does this mean we cannot have functions which take functions as arguments? I am trying to figure out how to expose egui bindings to wasm, and the egui api uses a lot of closures.

ui.child_ui(| ui | {
  ui.label("hello");
  ui.horizontal( | ui | {
    ui.label("first");
    ui.label("second");
  });
});

I don't understand how i can model this in wit files.

As a side note, how do i represent a resource in rust code?

view this post on Zulip Lann Martin (Nov 17 2023 at 13:26):

In guest Rust you will typically use a resource wrapper generated by wit-bindgen.

view this post on Zulip Lann Martin (Nov 17 2023 at 13:28):

There isn't any straightforward way to pass a closure between a wasm guest and host. There are a couple of ways to pass function pointers depending on what exactly you're trying to do.

view this post on Zulip Lann Martin (Nov 17 2023 at 13:30):

The egui snippet you pasted appears to be a builder pattern of sorts. You could probably translate it into a wit interface using resources, which may be what you're getting at.

view this post on Zulip code red art (Nov 17 2023 at 15:46):

thanks for the reply. I fell asleep thinking about this :D. egui is not using builds, its just fully based on passing closures.

I was hoping to compile the egui library as a component too and link to guest, but it won't seem to work either, as wit doesn't have the syntax for first class function passing
Maybe it was intentional to not allow functions to travel across component boundary?

view this post on Zulip Lann Martin (Nov 17 2023 at 15:52):

Maybe it was intentional to not allow functions to travel across component boundary?

This is a tricky subject. Most languages don't pass actual code around; in Rust's case a closure compiles down roughly to an anonymous function pointer and a struct containing the state captured by the closure. You could perhaps model that as a callback (a tricky subject on its own in the component model) plus a record for state, but that would be a significant R&D project to actually integrate into an existing language.

view this post on Zulip code red art (Nov 17 2023 at 15:54):

seems message got corrupted while travelling across the component boundary :upside_down:

view this post on Zulip code red art (Nov 17 2023 at 15:58):

understood. I will see if i can contribute/fork egui with a more ffi friendly api and bind that to wasmtime. thanks for the answers <3

view this post on Zulip Notification Bot (Nov 17 2023 at 15:59):

code red art has marked this topic as resolved.

view this post on Zulip Joel Dice (Nov 17 2023 at 16:17):

Seems to me that WIT resources are exactly the right tool to model callbacks. Say, for example. you wanted to model a callback that takes two parameters, an s32 and a string, and returns a bool. The resource would look something like:

resource callback {
  call: func(a: s32, b: string) -> bool;
}

The guest can then implement that resource in such a way that it contains a closure containing arbitrary state (e.g. struct MyCallback(Box<dyn Fn(i32, String) -> bool>)). When the host (or composed component) receives a handle to an instance of that resource, it can call the call method when appropriate and later drop it when it's no longer needed.

view this post on Zulip Lann Martin (Nov 17 2023 at 16:41):

Ah you're right, that wouldn't necessarily be so bad. It would require a different resource type for each unique closure signature but that could be semi-automated as well.

view this post on Zulip Lann Martin (Nov 17 2023 at 16:46):

Based on the above snippet:

resource ui {
    label: func(label: string)
    child-ui: func(cb: ui-callback)
    horizontal: func(cb: ui-callback)
}
resource ui-callback {
    call(ui: ui)
}

view this post on Zulip Lann Martin (Nov 17 2023 at 16:47):

You wouldn't want to have to execute that mess of callbacks very frequently, but hopefully it just builds a config object.

view this post on Zulip Victor Adossi (Nov 22 2023 at 02:54):

A bit late but even though the problem is basically solved w/ resources, I just had a thought -- what about solving it with WASM?

Assuming a self-hosting interpreter (let's say wasm3, or some future version of wasmtime), wouldn't a WIT definition (string) + some WASM bytes list<u8>) be a decent approximation for a free floating function? There's a lot of handwaving to do around how you'd deal with imports and the piping but it seems possible


Last updated: Dec 23 2024 at 13:07 UTC