@Luke Wagner @Alex Crichton question: should individual members on variants/enums be tagged with a @since
gate? - If this was Rust I'd say: "yes", since we have the #[non_exhaustive]
attribute which sets enums up to be forward-compatible. But I'm not sure what the thinking is for WIT around this. I don't think we have an equivalent attribute right now?
I can see this being relevant for things like error cases which we may want to extend later. Or for example in the case of HTTP there have been a number of new verbs added via RFCs over the years.
Right now the wit-tools
patch adding the @since
gates does not seem to support it, so I guess I was wondering what our thinking here was.
Oh I guess @since
also can't be added to individual struct fields right now either
Yep, that's right
Uh oh, I should have re-read the doc you wrote, shouldn't I?
(I'll go do that now haha)
I think the difference between Rust and WIT types here means that it doesn't make sense to put individual annotations on variants/fields because subtyping in the component model considers two structurally-the-same types equivalent, meaning that you don't have the same level of privacy in WIT that you can get in Rust
In other words, adding a variant is never backward compatible today, right?
correct, yeah
okk, I see! ty
that does seem tough though - If I'm understanding it correctly, that would then mean that post-WASI 1.0, if a new HTTP verb would be published, we wouldn't have a way to add it to wasi:http
without breaking the interface. I guess I was expecting at least some form of forward-compatibility?
that's correct, and that's more-or-less just how the component model works
we'd have to add new types for extenable-enums or extenable-structs to the component model to allow for such a use case
and/or a different versioning story
Alex Crichton said:
and/or a different versioning story
by that you mean: "WASI 1.0 doesn't mean no breaking changes forever after"? - e.g. folks would expect to work with potentially a 2.0, 3.0, etc.
IMO we're not far enough along to really answer that
no one wants continual breaking changes after a "one dot oh" no matter what
so that's at least the guiding light to me
yeah yeah yeah, that was my expectation too - I'm glad we're on the same page ^^
I was mostly trying to understand what you were saying - when you said: "different versioning story" my mind immediately went to: "oh, so.. breaking changes?"
The component-model repo used to have a "subtyping" section. The idea is/was that new fields (structs), cases (variants) and parameters (functions) could be added in a backwards-compatible way. I don't know where that section went, though.
oh interesting!
Found it. It was removed in Clarify what's in Preview 2. So I don't know if it was just temporarily removed for the preview 2 launch, or if it is a permanent change.
a new HTTP verb
There is a solution for this specific problem in place (along with any nonstandard verbs): https://github.com/WebAssembly/wasi-http/blob/a81c61fc584278f801393b22289840a439e51f50/wit/types.wit#L21
other(string)
yeah I can expand by saying that there's a few different ways we could go about making things extensible
e.g. a different way to update versions, a subtyping story, something more general like other(string)
, all of these are possible in theory
it's moreso that right now no such story exists and one needs to be created to enable a story for backwards-compatibly adding variants/fields
ok, yeah that makes sense to me
knowing that we've got options we can explore in the future is probably the higher-order bit in my question
ty for elaborating ^^
I still very much want to re-add subtyping so that it's possible to, in particular, add cases to variants in a non-breaking fashion. I initially added the Subtyping section because it seemed easy enough at the component-type level. But the problem is that just because a component-function-type f1
is a subtype of f2
, that doesn't mean that the source-level bindings are a subtype (or the Canonical ABI which sometimes shows through directly in the source-leve bindings), and thus if we say that a WIT-level change is semver-compatible, it may actually produce source-incompatible, and thus breaking, changes. So in the short-term we just disabled subtyping so we could sort it out more carefully later.
what's the upshot of the current situation? right now, we just need to define a new type?
Yeah, new type used by new function (or make a breaking version change and change the type/function in-place)
Part of the reason to take some time to figure out the right solution is that there are a couple of different paths:
There's one path where we try to support maximal subtyping (and thus API Evolution flexibility) by doing magic in the toolchain to capture the version you compiled against so that from "inside" the component, your bindings stay with a fixed type (even if the interface itself changes) until you intentionally bump it (and then opt into any source-level breaking changes). This seems like a cool sci-fi thing we could do, but it adds a new dimension of versioning and complexity to think about and so perhaps is too magic.
There's a more conservative path of only allowing subtyping when we have a reasonably-thought-out answer for how this form of subtyping can be implemented source-compatibly in all the languages we can think of, so that if source bindings always use the latest minor version, client source code never breaks. But this is a pretty stringent requirement when you consider, e.g., C, which is going to pretty much lay bare the whole Canonical ABI and thus the size of variants/records is an observable part of the interface; in the limit this rules out almost all subtyping. But perhaps there's some smart things we can do here to carve out just enough wiggle room for the cases we need.
Also, a built-in error
type (which might be a necessary part of the overall stream
feature) that supports a more dynamically-typed form of error case analysis (like anyhow::Error
) could subsume a bunch (but definitely not all all) use cases for extensibility in the 0.3 timeframe.
But this is a pretty stringent requirement when you consider, e.g., C, which is going to pretty much lay bare the whole Canonical ABI and thus the size of variants/records is an observable part of the interface
If a function returns a struct with 3 fields and you are subtyping this function to one that returns only two fields, as far as the canonical ABI is concerned on the callee side only those two fields really exist and thus contribute to the observable size, right? Both the caller and callee can have a different view of what fields exist. The glue code between components is responsible for adding/removing fields as necessary when translating.
Last updated: Dec 23 2024 at 12:05 UTC