[swift-evolution] [Pitch] `let` in protocols
Matthew Johnson
matthew at anandabits.com
Sun Jun 25 18:28:12 CDT 2017
I wrote a draft of a partial initializer proposal back in January or February of last year. If you're thinking of re-introducing that topic you might want to take a look at it. You can find it here: https://github.com/anandabits/swift-evolution/blob/partial-initializers/proposals/NNNN-partial-initializers.md. There was some discussion on the list as well. You might want to look up that conversation in the archives.
Sent from my iPad
> On Jun 25, 2017, at 2:24 PM, Robert Bennett via swift-evolution <swift-evolution at swift.org> wrote:
>
> You’re right Xiaodi, I did not entirely think this through. However I think this could still work with another addition to the language: partial initializers. These would be treated as initializers are now (meaning, for instance, no reference to self until initialization is complete) except they would only need to initialize a subset of the instance variables before exiting. An initializer could then satisfy the requirement that all instance variables must be initialized by calling partial initializers.
>
> By incorporating partial initializers, it would be possible to guarantee that a `let` variable in a protocol is only set once. This is because an init implemented in a protocol extension could delegate to a partial initializers required by the protocol, and because they are only partial initializers, they need not set the instance variables already set in the initializer; it would be up to a conforming type to ensure this is the caee. A confirming type would define the partial initializer to set everything not already set in the protocol extension’s init, and it would be a compiler error to set a variable in the partial initializer that is already set in the extension’s init without overriding the extension’s init. Example code:
>
> protocol P {
> var x: Int { let } // or { get set(init) }, or whatever
>
> partialinit initializeRest()
> init()
> }
>
> extension P {
> init() {
> initializeRest()
> self.x = 1
> }
> }
>
> struct S: P {
> let x: Int
> var y: String
>
> // It would be a compiler error to set x here without also redefining init()
> partialinit initializeRest() {
> self.y = “P has no knowledge of me”
> }
>
> // Can use default init provided by P
> }
>
>> On Jun 23, 2017, at 8:12 PM, Karl Wagner <razielim at gmail.com> wrote:
>>
>> What you want is some way to guarantee value semantics when writing generic code.
>>
>> It’s a known hole, and admittedly quite a big one. I hope that there will be time for core language improvements like this in Swift 5. Be sure to raise the issue again once planning for that starts!
>>
>> - Karl
>>
>>> On 23. Jun 2017, at 23:43, Robert Bennett via swift-evolution <swift-evolution at swift.org> wrote:
>>>
>>> Hello Swift Evolution,
>>>
>>> I’m bumping into an annoying problem with protocols. In a class or struct it is common to have a `let` instance variable and assign it in `init`. Unfortunately there is no way to translate this into a protocol with init in an extension. If attempting to set the variable in init in an extension, it must be of type { get set }, which means it cannot be a `let` constant in the conforming type. AFAIK there is no way around this — if you want to set an instance variable in an initializer in a protocol extension, it must be marked as { get set }. The alternative is to write the initializer separately for each adopting type, but this violates DRY.
>>>
>>> Hence, I am proposing a third option to go along with `get` and `set` in a protocol. This would indicate that the variable can be a constant, but is settable in an initializer. In this case, the conforming type *must* use `let` to declare the variable.
>>>
>>> Option 1: the keyword `let`. If present, it would need to be the only thing in the curly brackets because it simultaneously implies `get` and not `set`.
>>>
>>> protocol P {
>>> var x: Int { let }
>>> init(_ x: Int)
>>> func modifyX()
>>> }
>>> extension P {
>>> init(_ x: Int) {
>>> self.x = x // This is ok; would not be ok if x were marked { get }
>>> }
>>>
>>> func modifyX() {
>>> self.x += 1 // Not allowed
>>> }
>>> }
>>>
>>> struct S: P {
>>> let x: Int // This is ok; would not be ok if x were marked { get set }
>>> }
>>>
>>> Option 2: `set(init)`. Can (and often will) coexist with `get`.
>>>
>>> protocol P {
>>> var x: Int { get set(init) }
>>> init(_ x: Int)
>>> func modifyX()
>>> }
>>> extension P {
>>> init(_ x: Int) {
>>> self.x = x // This is ok; would not be ok if x were marked { get }
>>> }
>>>
>>> func modifyX() {
>>> self.x += 1 // Not allowed
>>> }
>>> }
>>>
>>> struct S: P {
>>> let x: Int // This is ok; would not be ok if x were marked { get set }
>>> }
>>>
>>>
>>> I’d like to hear all of your thoughts on this.
>>>
>>> Best,
>>> Robert
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170625/605da307/attachment.html>
More information about the swift-evolution
mailing list