You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The problem here IMO is the implicit self argument like in C++. C++ has to add a lot method specifier like const/&/&&/static to specify what this parameter a method took. And C++23 approved a feature called "deducing this" to fix this problem. I really wish this wasn't the case here.
It seems like the idiom of returning the self parameter is a rather language-specific syntactic workaround. That is, since the returned value is assumed to be exactly the same as the passed parameter value, you don't actually need to be passing an extra runtime handle value from callee to caller as a return value; the caller knows exactly what to use as the parameter to the next method call in the chain; it's just a matter of getting the nice syntax to do so.
Thus, I wonder if what we want here is a [builder] name prefix (as a further refinement to the existing [method] name prefix) that hints to the language bindings generator that this method should be callable in a builder style which, for some languages, means tacking on the self-parameter as a return value. This would both be more efficient (esp. with caller-side inlining) and would potentially allow better syntactic integration in a wider variety of languages by stating the interface author's real intent.
Sorry I don't know if I understand your comment correctly.
What im saying here is I think the rust way is the correct way to go.
In Rust, the self parameter of a method is explicit and can take several form.
&self
&mut self -> borrow<T>
self
mut self -> own<T> / consume
...other valid form
the return type can mix and match with self parameter. for example: both (&mut self) -> &mut Self and (mut self) -> Self allows chained builder calls.
from an user perspective, I would like to be able to express what I want to pass as self instead of an implicit borrow<T>.
As a side note: consume in issue #226 would be fine as a solution, although I would prefer explicit.
Side note 2: perhaps we need borrow<T> and mut-borrow<T> or perhaps (shared<T>/exclusive<T>/own<T>)?
I've run into nice builder patterns in Rust, around Transport Layer Security (TLS) I think, where the builder pattern implemented a state machine, so methods didn't necessarily return Self, but a new, or "next", builder type, with the next builder type being slightly different, to represent the progression through the building state machine.
I've also run into builder patterns where I felt forced to store the builder to a local and then call mutating methods on it. Didn't feel as natural but I realized I just had to write the code once and then I could forget it so it didn't matter much.
Also some builder methods return a Result, so it was nice to be able to have the function return an Error, even from a chained list.
And being able to throw in a conditional test in the middle of builder calls is useful. So sometimes wanting to store the intermediate builder value in a local so the conditional block can be run or not on it, before continuing with the finalizing builder steps.
So there are benefits to supporting fn(&mut self, ...) functions and fn(self, ...) -> Self, and fn(self, ...) -> NextBuilder patterns.
But should the Component Model have to spell out something special for the builder pattern? (I know I'm not able to take many other language constraints into account so maybe the answer is yes but it is surprising.)
But getting back to the original question or issue. Naively, it is surprising a Resource can't be consumed in some form of a signature. The difficulty must be on the callee side, in doing something with the resource instantiation that should now no longer exist. An interesting problem. Seems like the API around Resources will be super important.
If I understand the builder pattern correctly, the steps can be reordered, so you need to generate as many permutations of the step-builder-wrapper as possible.
To express the builder pattern at the type level, there is even a special design pattern called State Type (or Generic as Type Class Pattern).
This is a very high-level type application, and I think it is more realistic to directly support move semantics(self: own<T> in question).
Activity
lukewagner commentedon Apr 8, 2024
It seems like the idiom of returning the self parameter is a rather language-specific syntactic workaround. That is, since the returned value is assumed to be exactly the same as the passed parameter value, you don't actually need to be passing an extra runtime handle value from callee to caller as a return value; the caller knows exactly what to use as the parameter to the next method call in the chain; it's just a matter of getting the nice syntax to do so.
Thus, I wonder if what we want here is a
[builder]name prefix (as a further refinement to the existing[method]name prefix) that hints to the language bindings generator that this method should be callable in a builder style which, for some languages, means tacking on the self-parameter as a return value. This would both be more efficient (esp. with caller-side inlining) and would potentially allow better syntactic integration in a wider variety of languages by stating the interface author's real intent.WDYT?
serephus commentedon Apr 9, 2024
Sorry I don't know if I understand your comment correctly.
What im saying here is I think the rust way is the correct way to go.
In Rust, the
selfparameter of a method is explicit and can take several form.&self&mut self-> borrow<T>selfmut self-> own<T> / consumethe return type can mix and match with self parameter. for example: both
(&mut self) -> &mut Selfand(mut self) -> Selfallows chained builder calls.from an user perspective, I would like to be able to express what I want to pass as self instead of an implicit
borrow<T>.As a side note:
consumein issue #226 would be fine as a solution, although I would prefer explicit.Side note 2: perhaps we need
borrow<T>andmut-borrow<T>or perhaps (shared<T>/exclusive<T>/own<T>)?serephus commentedon Apr 9, 2024
Anyway, this is just a thought for your consideration, I'm not very familiar with wasm/wit. I hope it make sense.
FrankReh commentedon Apr 9, 2024
I've run into nice builder patterns in Rust, around Transport Layer Security (TLS) I think, where the builder pattern implemented a state machine, so methods didn't necessarily return Self, but a new, or "next", builder type, with the next builder type being slightly different, to represent the progression through the building state machine.
I've also run into builder patterns where I felt forced to store the builder to a local and then call mutating methods on it. Didn't feel as natural but I realized I just had to write the code once and then I could forget it so it didn't matter much.
Also some builder methods return a Result, so it was nice to be able to have the function return an Error, even from a chained list.
And being able to throw in a conditional test in the middle of builder calls is useful. So sometimes wanting to store the intermediate builder value in a local so the conditional block can be run or not on it, before continuing with the finalizing builder steps.
So there are benefits to supporting
fn(&mut self, ...)functions andfn(self, ...) -> Self, andfn(self, ...) -> NextBuilderpatterns.But should the Component Model have to spell out something special for the builder pattern? (I know I'm not able to take many other language constraints into account so maybe the answer is yes but it is surprising.)
But getting back to the original question or issue. Naively, it is surprising a Resource can't be consumed in some form of a signature. The difficulty must be on the callee side, in doing something with the resource instantiation that should now no longer exist. An interesting problem. Seems like the API around Resources will be super important.
lukewagner commentedon Apr 9, 2024
FWIW, the linearly-typed chaining you're describing is currently expressible via, e.g.:
What's missing is the nice syntactic sugar that lets you write a nice terse method call chain.
oovm commentedon May 12, 2024
If I understand the builder pattern correctly, the steps can be reordered, so you need to generate as many permutations of the step-builder-wrapper as possible.
To express the builder pattern at the type level, there is even a special design pattern called
State Type(orGeneric as Type Class Pattern).This is a very high-level type application, and I think it is more realistic to directly support move semantics(
self: own<T>in question).