If that is the only explanation for your (but maybe not others) concern then I donât understand how something like this can be justified as a dealbreaker.
I still would like to see anyone providing a concrete code example where it would be critical to have a wrapped property but also a totally different and unrelated property that shares the exact same identifier as proposed for private backing storage which then would collide.
A.swift
class Test {
@MyWrapper var t = ""
}
private func test() {
let d = \Test.$t
}
B.swift
private func test() {
// let t = Test()
let d = \Test.$t // cannot access
}
C.swift
private func test() {
let t = Test()
let d = \Test.$t // can access
}
Is there any plan to use Property Wrapper to enable simpler Codable usage where one wants to specify a JSON key being different from the name of just one property?
That would really be awesome, because AFAIK currently Codable Dora not offer me to use the synthesized init/encode function and keys if I wanna change just one key. For bigger types, with e.g. 10 properties (JSON fields) this is very cumbersome. Property Wrapper might solve this?
You donât need real world examples to dislike names that could (theoretically) collide - imho that is a question that is more fundamental.
Objective-C has a history of âmagicâ names (property setters, and maybe more), but imho itâs no good fit for Swift when changing names has side effects on other properties of a type.
Take inherited methods as example:
How likely is it that you implement a totally different and unrelated method with an identical name?
Still, Swift demands that you override explicitly.
Memory layout is another example: It is actually a property of a given type - but afair, it isnât modeled that way because of possible interference with static properties.
If we would reject every SE proposal just because some people say "why not?" as an explanation for their concern we wouldnât have a language we have today.
If people cannot present any possible concrete examples (even hypothetical pseudocode), I donât buy these contra arguments. To be clear I only speak for myself here.
Any form of identifier name for the backing storage is a subject for a collision because "who says that in the future such identifier wonât be allowed as valid user code"!?
Every variable may collide with an other variable. If you encounter such case, just do as you do right now, change the name of one of the two variables. That's not like if the compiler where imposing you a fixed name on all types (what it would do for memory layout).
We are talking about a name that can be changed easily as it is based on the wrapped variable name, and that will be generated only when a type contains a property wrapper.
So, if I make a MyWrapper type that goes around an Int, and use it for a property I call "value":
value accesses the Int.
_value would access the MyWrapper if it's used in an accessible context.
$value lets me configure the wrapper, its type is Self by default, but I can change it to any other type by specifying the projectedValue property.
If a projection is used, I must use it; I have no way to override the decision of MyWrapper's author to use a proxy type.
A previous version of the proposal used to have a $$value to override my previous point, but we can't do that now.
If stacked wrappers are used, we can only access the projection of the outermost one? Or can we get to the intermediate ones, whether or not they use Self?
Why is the substitute feature called a "projection"? Wouldn't "proxy" be better?
$value only will exist if your MyWrapper implements projectedValue but that is optional, nor is it a requirement that it returns Self. In the previous version the user cannot override what the wrapper projected if you are not its implementor. You also donât need to use $value if there is a projection, in private context you can still use _value to access the wrapper directly. That said you can also access or expose nested projectedValue of nested property wrappers.
I apologize if this was already discussed. Itâs getting hard to keep up with this :) if it was, feel free to just point me in the right direction. I figure itâs worth bringing it up just in case.
My question is, how do property wrapper affect reflection on an object? If not, should they? Maybe a library author might want to take advantage of that.
If your not the creator of the wrapper type then no, module clients are not allowed to retroactively extend wrapper types they donât own with projections like projectedValue, because they donât have access to the private backing storage the compiler creates for the module wrapped properties.
One could argue that it should be possible on your own properties that you wrap, but that can be left for future discussion. (See my next post.)
I must apologize for some confusion, I will edit my previous post. The nesting wrapper does not need to expose the access because the nested wrapper type will be accessible through its wrappedValue. Therefore you only need to traverse through outermost wrapper and all the nested wrappedValue up to the wrapper type of your interest and explicitly access its projectedValue.
I omit initaliters for the simplicity of the example:
@propertyWrapper
public struct W<Value> {
public var wrappedValue: Value
// this property is optional to implement
public var projectedValue: AnyTypeYouWant
}
@propertyWrapper
public struct IntWrapperWithStringProjection {
public var wrappedValue: Int
public var projectedValue: String { "\(wrappedValue)" }
}
public struct Example {
@W @IntWrapperWithStringProjection
public var value: Int = 42
// results into
private var _value = W<IntWrapperWithStringProjection>(initialVakue: .init(initialValue: 42))
public var value: Int { ... }
// likely wrong type, see my post bellow
public var $value: AnyTypeYouWant { ... }
public func getInnerStringProjection() -> String {
return _value.wrappedValue.projectedValue
}
}
Edit: I will edit this example when I get a clarification on the projection rule on composed wrappers (see my post bellow).
@CTMacUser actually now that i wrote the above example, Iâm not sure which type $value will have, either String or AnyTypeYoutWant.
@Douglas_Gregor the proposal needs to clarify which projectedValue will be projected in case of wrapper composition. It is weird that value is from the inner most wrapper but the projection from the outer most wrapper in my example. I tend to say it should come from the inner most wrapper.
The implication would be that if the inner most wrapper has nothing to project, but the outer most or the nearest ancestor wrapper has something to project, that the compiler wonât do anything as the rule will create only a projection for the wrapper that also matches the wrapper properties type.
That said, I think I made a mistake in the desugaring, as $value should be of type String instead.
// correct desugaring
public var $value: String {
get { _value.wrappedValue.projectedValue }
set { _value.wrappedValue.projectedValue = newValue }
}
// wrong desugaring
public var $value: AnyTypeYouWant {
get { _value.projectedValue }
set { _value.projectedValue = newValue }
}
class ConcreteType {
@MyWrapper var myVar: MyClass
}
// use-site of projection:
let obj: ConcreteType = ...
let pro: MyProjection = obj.$myVar
Some time later I might refactor and introduce a protocol which should give access to both the property and the projection - I could do this:
protocol MyProtocol {
var myVar: MyClass { get }
var myVarProjection: MyProjection { get }
}
extension MyClass : MyProtocol {
var myVarProjection: MyProjection {
return $myVar
}
}
But this will require to change all call-sites where $myVar was used before to myVarProjection:
let obj: MyProtocol = ...
let pro: MyProjection = obj.myVarProjection
What about a way to make $ available for protocol oriented programming styles:
protocol MyProtocol {
@projected(MyProtocol) var myVar: MyClass { get }
}
// or alternatively:
protocol MyProtocol {
var myVar: MyClass { get $(MyProtocol) }
}