At first I was disappointed to hear this but then I am glad you've said it. Good luck!
Considering the extensive discussion around this feature, whenever @Douglas_Gregor’s next revision of the proposal lands, it might be good to be more specific on which parts of the proposal the community feedback is still relevant and also remind people which parts are unlikely to change. Thanks for all the work so far to all of you.
That would have not be my first choice, but considering the constraints, I too find it a tolerable alternative.
For the third step we could do something like this if we want to keep the prefix $:
$something<doSomethingSpecial>()
To declare it in the propertyWrapper a valueWrapper subscript might be used.
I'll make a point of it.
Agreed
I'm sorry I've been away from this thread since last night - looks like there is lots of discussion. Separate from the "grand strategy" discussions, I think that this rebuttal to my proposal is right on the mark:
No - I agree with you that this isn't very different, and you're right that this is close enough to provide the sorts of APIs that I'm imagining that it is probably not worth a "step three". Doug also pointed out that having yet-another scoping name lookup system isn't a desirable thing, which is another -1 to me for the property$foo
approach. Therefore, I retract this "three step" proposal idea - I don't believe in it now either, thank you all for the discussion!!
Given all that, moving the location of the dollar sign isn't highly motivated, and I can see the argument that invalidating a bunch of videos has a high cost that isn't balanced by a corresponding foreseeable evolution win, so I retract my concern about that.
I'm still very much in favor of the refinements to the model that Doug is proposing upthread, and I still think that "variable wrapper" is a more technically correct name for this.
-Chris
As others have also pointed out, this works when $
always returns self.
Maybe $
should not be a user definable computed property then but all projections be explicit?
It can be made explicitly to return self, as @Chris_Lattner3 did.
For something this complex and (I believe) uncommon, it shouldn't be too much burden when many other cases want to just expose one more value.
Right, you could use another type to further refine the interface, e.g.:
struct MyWrapperProjection<T> {
init(_ : MyWrapper<T>) {...}
public var dbhandle : DBHandle<Value> {... }
public var view : MyView<Value> {... }
public var binding : MyBinding<Value> {... }
}
@variableWrapper
enum MyWrapper<Value : MyProtocol> {
var wrappedValue: Value { ... }
public var $: MyWrapperProjection { return MyWrapperProjection(self) }
}
-Chris
Right. As I mentioned upthread, the one thing this gives up is the ability to have a “primary” projection and secondary projections that are not part of the interface of the primary projection type. Short of a compelling use case that requires all of the above I think we can live with a single projection. It keeps an already magical feature much simpler than it would be we supported n projections directly from the wrapper.
The catch is that it needs to return a Binding when you are working with a State property in SwiftUI.
The main issue is that once you define a type with a primary projection, adding new projection now become a breaking change, while it would not be if it was not required to return self for the primary one.
It's not necessarily a breaking change. But it is if you don't want it to live on the primary projection.
FWIW, I think the coolest thing about Chris's 3rd phase idea is that if you squint, $
is a bit like .
. You could consider it to be a member access operator for projected members. This is especially the case if we include the idea from @DevAndArtist to support methods as well as properties. In this design the wrapper type is able to offer API that almost appears as if it were part of the wrapped property. It can do this without conflicting with members of that type because the $
member access operator is used instead of the usual dot.
This way of thinking about it does give it some appeal. Instead of offering a foo$
projection, we might see wrappers that just expose a couple of members. For example, maybe all a wrapper type needs to expose is foo$addObserver
. This keeps the surface of the wrapper type's API leaner which may be easier for users to understand - there is no need for an intermediate projection type.
All that is probably besides the point now - it would feel clunky to have $foo
and foo$projection
- but it's at least worth observing the option and making an intentional decision about it.
And I guess that $myState.binding
is too cumbersome for this common case.
But I like that it’s not too magical and Uniform for all projections.
Thank you so much for summarizing the general idea and putting it into more understandable words that I might have written above. It would be too good if we had such a feature in Swift. The property wrapper can remain private and have a simple identifier prefixed with an underscore, while the most interesting parts would use a special character for altering the access behavior we know today by using a .
I don‘t know FP operators that well, but instead of $
there are other characters that could fit as well. But theoretically we don‘t need to use $
here anymore, we just need a new accessor character for projections instead of the usual .
property^projectedMember
property:projectedMember
property@projectedMember
property~projectedMember
Having such a projection behavior solves even more problems of the current design: retroactive extensions of projections, projected methods.
It‘s a little sad that the idea was dropped so fast from the author.
I was thinking along this line too, but trying to integrate it with the practical restriction that we need to support leading $
too.
What I was thinking, though was:
-
Use trailing
.$
rather than trailing$
, for better readibility (though it's more to type when writing source code). -
Use trailing
.$foo
for additional projections. -
Define leading
$
to be a synonym for trailing.$
.
That way, the more general property wrapper proposal can proceed with Swift community input, producing something that is extendable in the ways people have been asking for, but the existing more specific syntax would continue as sugar on the larger proposal's syntax.
The only real issue I see with that approach is the question of whether there's meaningful ambiguity in cases like a.$b
. (Is this a named projection from a
, or a default projection from a.b
?) It might be sufficient just to say that leading $
takes precedence over trailing .$
.
Excuse me for balling out loud, but why would projections need to stop on computed properties, we could go beyond that and allow projections on subscripts which would create a totally new kind of operations, but this idea seems to be thrown away too easily.
collection[index]$projectedMember
collection[index]^projectedMember
collection[index]~projectedMember
A generalized feature would always be better, while wrapperValue
always felt like a quick workaround hack.
It does feel clunky to have both should we add multiple projection later. That said, without enough motivating cases it’s hard to wrap around what multiple projection should look like, and I think that having primary projection along side multiple projection (in the same wrapper) would not be particularly easy to use, and perhaps it would do well to sport something more visually distinct from single projection.
I'd also like to state a personal dislike for "variable wrapper" as the name of this feature, because I don't think it clearly covers properties. The underlying thing is a storage provider, and variables and properties can provide storage in different ways.
For that reason, I'd much prefer to use "storage wrapper", as the most accurate description I've seen proposed so far.
Also, FWIW, I think there's some danger in the word "wrapper". In general, an "XYZ wrapper" could mean an XYZ that wraps something else, or a thing that wraps an XYZ. I don't think I even know which of the two the SE-0258 proposal really wants it to be.