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

Xiaodi Wu xiaodi.wu at gmail.com
Sun Jun 25 14:33:49 CDT 2017


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/eb2e46c8/attachment.html>


More information about the swift-evolution mailing list