[Pitch #3] Property wrappers (formerly known as Property Delegates)

I think this is a good model. It puts the control in the hands of the author of the property wrapper type.

Presumably, this follows the rules of definite initialization, the same way as we do for assignments to foo becoming initializations when possible.

Your intent here is to do a sequence of .wrappedValue's up to the one .wrapperValue? I'm not sure if I want that or the outermost .wrapperValue, and would want use cases.

Doug

& already has an established meaning for inout. We shouldn't overload the existing syntax to mean an arbitrary binding, or we'll introduce more type checking ambiguities.

Doug

3 Likes

FWIW, the original proposal was either internal or "access level of the original property", whichever was more restrictive. I think I'd like to get back to that.

Doug

10 Likes

Would this then require var wrapperValue: Self { return self } when we want to expose API on the wrapper itself?

@Douglas_Gregor when do you think the next review round will be kicked off? Before that we should have discussed the issue with value mentioned upthread and if we should rename it so it unlikely collides when @dynamicMemberLookup is used. I proposed wrappedValue.

2 Likes

Would the initial implementation expose a way to increase the access level past internal?

Since the whole access is routed through that property, it will also require you to provide a setter if you want your property wrapper to be settable. But even that example you wrote feels redundant to me and I‘d rather stick with the current design where this property is optional to implement.

What do you mean by settable?

I want the following example to work, even in a world where $foo is only available when wrapperValue is defined.


@propertyWrapper
struct Wrapper<V> {
    let value: V

    var wrapperValue: Wrapper { return self }

    init(initialValue v: V) { self.value = v }
}

extension Wrapper where V: BinaryInteger {
    func valuePlus3() -> V { return value + 3 }
}

struct S {
    @Wrapper var x: Int
}


let s = S(x: 4)
print(s.x)                        // 4
print(s.$x.valuePlus3())          // 7
1 Like

I meant mutable by settable. If your example had another mutating member, you wouldn‘t be able to use it with your current rules and the way you defined wrapperValue.

That's fine. If a mutable func or a var property were added, then obviously a setter would be required. That's no different than any other struct property whose properties are mutable in some way.

Correct, the only advantage I see in requiring explicit wrapperValue is that you can limit the access to the theoretically mutable stored property to be read only. However if we‘d require it, it could lead to confusion with wrappedValue as the difference is a single character in the middle (assuming that we‘d rename value). To me it‘s not a big deal, but I don‘t know about other if they see wrappedValue and wrapperValue.

I am not an advocate of forcing wrapperValue to exist to get access to $foo. I just want to clarify that it will still be possible, even if it means wrapperValue just returns self.

I see, but you can test your theory already, just create such a wrapper and print something in the getter. :slightly_smiling_face:

Could we please get a post listing the current syntax as implemented in the latest toolchain for 5.0.x and the Xcode 11 beta? wrapperValue isn't doing anything for me in the beta, and neither is delegateWrapper.

I did. Hence the example I posted above. I just want confirmation that this will work after any changes are implemented. Frankly, it's getting rather hard to determine which behavior is expected or missing. The two implementations and the proposal have diverged from each other.

The latest changes on master should reflect the proposal, but the toolchain with xcode 11 has an old revision from a previous iteration.

The toolchain from June 7 does not recognize @propertyWrapper. If there are changes which have not yet made it into a toolchain, I don't have a way to test them.

@Douglas_Gregor I may have missed responses to this question, but is anything being done to allow direct initialisation of a property wrapper value when its initialiser has other arguments? Right now, as soon as we need extra arguments to a property wrapper, we can't directly initialise the property.

I want to be able to write:

@MyPropertyWrapper(name: "test") var property = 6

But I'm forced to use:

@MyPropertyWrapper(name: "test", initialValue: 6) var property: Int
8 Likes

This works just fine with Xcode 11 included toolchain on Catalina.

@propertyWrapper
struct Test {
  // I propose to rename it to `wrappedValue`
  let value: Int
  
  // Should be `wrapperValue`.
  var delegateValue: String { "<\(value)>" }
  
  init(initialValue: Int) {
    self.value = initialValue
  }
}

struct Foo {
  @Test var number = 42
}

let foo = Foo()
print(type(of: foo.$number), foo.$number) // String <42>

After refactoring my code to avoid a compiler crash, I got it working.

1 Like