[swift-evolution] [Pitch] `let` in protocols

Robert Bennett rltbennett at icloud.com
Sun Jun 25 14:24:04 CDT 2017


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
> 


More information about the swift-evolution mailing list