Stream: general

Topic: Wit export and interface differences


view this post on Zulip Paul Scheduikat (Feb 01 2024 at 10:21):

I don't really understand why there is a difference between export and interface in .wit. Can anyone explain the thinking behind it? I think all exported functions should be able to be imported by other components, or am I missing something?

view this post on Zulip Viktor Charypar (Feb 01 2024 at 10:25):

As I understand it, interfaces are a definition of a collection of related functions and types. Exports are a claim that a component (which implements the world in which the export is) provides an implementation for that interface (and imports are a claim that it relies on them and requires them to be provided).

view this post on Zulip Paul Scheduikat (Feb 01 2024 at 10:36):

So an interface can be used to export the same signature from multiple worlds.

From the docs it seems like to import a function of a component from another component, there needs to be a defined interface.

What does a .wit file with a defined export but no interface do then? Export a non-importable function?

view this post on Zulip Viktor Charypar (Feb 01 2024 at 10:45):

There's _always_ a side importing the interface and a a side exporting it. Often, one of them is the host (e.g. wasmtime), but when composing components for example, one will import the interface which the other exports, so that the former can call the latter.

You're right that in order to use a function from another component, there needs to be an interface, otherwise they won't know how to call each other, exchange data and what the format of it is, that's what the WIT is doing. The need for it makes more sense when you think about the scenario where the two components are built in different languages with different type systems and data representations.

view this post on Zulip Viktor Charypar (Feb 01 2024 at 10:46):

Wit file with just an export will generally be part of a package with other wit files, or references to external definitions of interfaces (like WASI). So that file is just describing the world of the component in terms of existing interfaces.

view this post on Zulip Paul Scheduikat (Feb 01 2024 at 10:59):

I am reading this tutorial right now.

If this add:

package docs:adder@0.1.0;

interface add {
    add: func(a: u32, b: u32) -> u32;
}

world adder {
    export add;
}

Is imported by:

// in the 'calculator' project

// wit/world.wit
package docs:calculator;

interface calculate {
    eval-expression: func(expr: string) -> u32;
}

world calculator {
    export calculate;
    import docs:adder/add@0.1.0;
}

and referenced by:

// Bring the imported add function into scope
use bindings::docs::calculator::add::add;

Why couldn't this add:

package docs:adder@0.1.0;

world adder {
    export add: func(a: u32, b: u32) -> u32;
}

be imported by:

// in the 'calculator' project

// wit/world.wit
package docs:calculator;

interface calculate {
    eval-expression: func(expr: string) -> u32;
}

world calculator {
    export calculate;
    import docs:adder/adder@0.1.0;
}

and referenced by:

// Bring the imported add function into scope
use bindings::docs::calculator::adder::add;

view this post on Zulip Viktor Charypar (Feb 01 2024 at 11:05):

package docs:adder@0.1.0;

world adder {
    export add: func(a: u32, b: u32) -> u32;
}

Is defining a world, which exports a single function, which isn't part of an interface, I think? You should be able to import add: func as the counterpart to that, if I imagine things correctly.

view this post on Zulip Paul Scheduikat (Feb 01 2024 at 11:07):

I don't think so: "However, to use your component from another component, it must export an interface. "

view this post on Zulip Viktor Charypar (Feb 01 2024 at 11:17):

Hmm, fair enough. I've only ever done this with interfaces, never tried without

view this post on Zulip Paul Scheduikat (Feb 01 2024 at 12:02):

Actually I'm now confused as to why this:

import docs:adder/add@0.1.0;

Works at all, I first thought it would import:

interface add {
    add: func(a: u32, b: u32) -> u32;
}

However that can't be the case, because then how would it be know which implementation to call? So most likely it's importing the add implementation, from here:

world adder {
    export add; // this is imported
}

This would be a bit surprising to me, I would think this would need to be imported as:

import docs:adder/adder/add@0.1.0;

Is the adder world special and because it's the same name as the package name, the repetition of adder can be avoided?

Or are exports in different worlds "global", so this would actually be an invalid .wit file?

package docs:adder@0.1.0;

interface add {
    add: func(a: u32, b: u32) -> u32;
}
world adder {
    export add;
}
world anotherAdder {
    export add;
}

view this post on Zulip Viktor Charypar (Feb 01 2024 at 12:07):

Other folks might know better, but I'm relatively sure import and export without other qualifiers refer to interface names. Fully qualified it's namespace:package/interface@version, if it's a single word, I'd expect it to be an interface in the same package as wherever the reference is made

view this post on Zulip Viktor Charypar (Feb 01 2024 at 12:08):

As far as I know, the worlds are generally used for binding generation - the code of the component will say "I implement this world", which is short for "these are the things I import/export". At least that's how I think about it

view this post on Zulip Viktor Charypar (Feb 01 2024 at 12:09):

Anyone better informed, please correct my rough understanding :pray:

view this post on Zulip Paul Scheduikat (Feb 01 2024 at 12:15):

From that would follow that there can only be a single implementation of an interface. Which would also be surprising to me, can someone else clarify this?

view this post on Zulip Viktor Charypar (Feb 01 2024 at 13:00):

A single implementation of an interface in a single component/world. That would make sense to me. Is that a problem?

view this post on Zulip Paul Scheduikat (Feb 01 2024 at 13:13):

That would make sense to me too, however you're saying interfaces definitions are imported directly and not via an implementation, meaning a single implementation per package. How would I import anotheradder.add if I can only reference the add interface directly?

view this post on Zulip Paul Scheduikat (Feb 01 2024 at 13:31):

If I do this:

package example:adder;

interface add {
    add: func(a: u32, b: u32) -> u32;
}

/// An example world for the component to target.
world adder {
    export add;
}

world another {
    export add;
}

I get this error, so multiple implementations seem possible I'm just a bit lost on how to select a "default" one.

$ cargo component build --release
error: failed to create a target world for package `adder` (/home/luap/Code/add/Cargo.toml)

Caused by:
    0: failed to select the default world to use for local target `/home/luap/Code/add/wit`
    1: multiple worlds found in package `example:adder`: one must be explicitly chosen

view this post on Zulip Paul Scheduikat (Feb 01 2024 at 13:36):

I got it, there is a separate world field in cargo.toml.

view this post on Zulip Peter Huene (Feb 01 2024 at 13:37):

Would you mind filling an issue for cargo component to make that error message more actionable? I'd appreciate it!

view this post on Zulip Paul Scheduikat (Feb 01 2024 at 13:44):

Sure https://github.com/bytecodealliance/cargo-component/issues/227

I had this .wit file: package example:adder; interface add { add: func(a: u32, b: u32) -> u32; } /// An example world for the component to target. world adder { export add; } world another { export...

view this post on Zulip Peter Huene (Feb 01 2024 at 14:27):

Thank you!


Last updated: Oct 23 2024 at 20:03 UTC