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

Xiaodi Wu xiaodi.wu at gmail.com
Sun Jun 25 15:17:15 CDT 2017


The protocol extension does not have to be in the same module as the
protocol itself. How will the compiler know what variables are set in the
partial initializer when it is compiling the protocol or the struct?
On Sun, Jun 25, 2017 at 15:13 Robert Bennett <rltbennett at icloud.com> wrote:

> For a concrete type, a partial initializer is a function that:
> Can set `let` instance variables
> May not refer to `self` in an rvalue
> Must set the same subset of instance variables regardless of the code path
> taken
>
> For a protocol, a partial initializer is simply a function that sets *any*
> subset of the instance variables. A protocol could provide a default
> implementation of one. In order for this `let` proposal to work, though, if
> a `let` variable is set in the protocol extension’s implementation of a
> (full) initializer, the protocol would need to provide at least one partial
> initializer that it does not provide an implementation for, so that a
> conforming type can fill in the gaps for all of the instance variables it
> defines that the protocol doesn’t require/know about. That way the compiler
> can have faith that a conforming type will be able to fully initialize
> itself with the default implementation of the required full initializer.
>
>
> On Jun 25, 2017, at 3:57 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>
> So your proposed definition of a partial initializer is one that does not
> set any protocol requirement?
> On Sun, Jun 25, 2017 at 14:54 Robert Bennett <rltbennett at icloud.com>
> wrote:
>
>> The compiler would only raise an error when a conforming type violated
>> the requirement that the partial initializer not set x. The protocol itself
>> would never fail to compile.
>>
>> On Jun 25, 2017, at 3:33 PM, Xiaodi Wu <xiaodi.wu at gmail.com> wrote:
>>
>> On Sun, 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.
>>
>>
>> How can the compiler raise this error when the implementation of the
>> partial initializer does not even have to exist at compile time?
>>
>> Example code:
>>>
>>> protocol P {
>>>     var x: Int { let } // or { get set(init) }, or whatever
>>>
>>>     partialinit initializeRest()
>>>     init()
>>> }
>>>
>>> extension P {
>>>     init() {
>>>         initializeRest()
>>>
>>
>> How can the compiler ensure that `initializeRest()` does not already set
>> `x`?
>>
>>
>>>         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/e4bdecde/attachment.html>


More information about the swift-evolution mailing list