WebAssembly / component-model

Repository for design and specification of the Component Model
Other
965 stars 79 forks source link

WIT - inline definitions #135

Open guybedford opened 1 year ago

guybedford commented 1 year ago

Not pressing by any means, more nice-to-have, but thought I'd post while I'm thinking about it.

Just like one can define an inline tuple or result, it could be useful to define inline definitions.

That is, instead of:

enum error {
  error-a,
  error-b
}
record param-type {
  name: string,
  value: option<string>
}
func p: (param: param-type) -> result<_, error>

Writing:

func p: (param: record {
  name: string,
  value: option<string>
}) -> result<_, enum {
  error-a,
  error-b
}>

Where the above could be internalized as sugar for some anonymous type naming scheme (ideally derived from the function and param name itself, as opposed to its structure).

lukewagner commented 1 year ago

I totally agree. This also dovetails perfectly with the changes in #129 to keep Wit co-expressive with what you can write in an arbitrary .wasm component (so that you can get a faithful Wit generated from an arbitrary component, whether or not that component was originally generated using Wit). In particular, with #129, there are two cases for the types that are referenced by a function:

guybedford commented 1 year ago

I was imagining this being more of a sugar in the WIT syntax, than actually reflecting into the component, but that's interesting to hear if there might be overlap with the actual component representation.

Would be interesting to hear @alexcrichton's perspective on this as well.

alexcrichton commented 1 year ago

The *.wit tooling actually historically supported this but it was removed after what I thought was consensus that it shouldn't be supported for the primary reason that anonymous types like this typically can't be represented in target languages. Given how the component binary format is structured and validated today generating a *.wit from a component is not possible, however, due to the inability for *.wit to express anonymous types. That, to me, means that something should change.

I don't personally mind too much one way or another, but if the decision is to support anonymous types then I think that should also be coupled with a rough plan for bindings generation. I don't think the most naive solution, for example enum Type3 { ... } in Rust, is a suitable because I don't think that anyone would actually want to use that and it would basically be an antipattern in *.wit files.

guybedford commented 1 year ago

Could this be treated as sugar for something like a macro expansion generation.

For example from:

func p: (param: record {
  name: string,
  value: option<string>
}) -> result<_, enum {
  error-a,
  error-b
}>

being automatically expanded as if one had written:

enum myinterface-p-error {
  error-a,
  error-b
}
record myinterface-p-param {
  name: string,
  value: option<string>
}
func p: (param: myinterface-p-param) -> result<_, myinterface-p-error>

This way types within the component model itself remain non-anonymous, while this is treated more as a WIT feature?

alexcrichton commented 1 year ago

That makes sense to me yeah, and the follow-up from that would be to verify, at the binary layer, that all types are named unless they're primitives/lists/etc.

lukewagner commented 1 year ago

Ah, right, I keep forgetting about the bindings generation problems for fully general records and variants for many languages, thanks Alex. I like the idea of component validation additionally requiring that all such value types (iiuc, the list is: record, variant, union, enum, flags?) are required to be exported (which is already necessarily required for resource types) so that way component types are "in sync" with Wit on what's expressible such that you can render a Wit from a component. (This is also a conservative starting point, since we can always relax the validation requirements later.)

In that case, I think Wit should probably not offer the additional inline type syntactic sugar since it will be synthesizing a type name that will be semantically visible to the consumers of the component.