[swift-evolution] [Pitch] `let` in protocols
Jay Abbott
jay at abbott.me.uk
Tue Jun 27 11:00:58 CDT 2017
I think if we start to discuss composition as part of the language, it
could solve this issue.
In my composition pitch I didn’t go into great detail (and nothing about
properties in components at this early stage - I figured I should start
simple and we can think it through further as a group), but if we had a
syntax for composing types out of components (implementations of
protocols), you could include a guarantee that a component were initialised
in init. This would also solve the partial initialisation issue - as a
component is a complete implementation of a particular protocol, which can
be fully initialised itself, before the composed type is fully initialised.
On Tue, 27 Jun 2017 at 00:00 Karl Wagner via swift-evolution <
swift-evolution at swift.org> wrote:
>
> On 26. Jun 2017, at 13:44, Karl Wagner <razielim at gmail.com> wrote:
>
>
> On 25. Jun 2017, at 22: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.
>
>
> I’ll say again: I think you’re approaching this from the wrong angle. You
> want stronger guarantees about mutability of properties, but the answer is
> not to make protocols be more restrictive about how conformers are written.
>
> What you are really asking for is the guarantee that some type which
> conforms to SomeProtocol has value-semantics. Once you have that guarantee,
> you know that you have a unique instance whose properties will only mutate
> if they are stored in a “var” property and you yourself mutate it.
>
> For example, an Array’s “count” property is technically a computed
> property, but in a “let”-declared Array you can treat it as a “let”
> constant because Arrays have value semantics. Your guarantees about
> properties only being set in the initialiser would automatically be
> fulfilled without the need of any additional alternate initialisation
> patterns.
>
> - Karl
>
>
> Just to expand on this a little bit, as I see it there are two parts to
> your problem. First, you were asking for some kind of
> partial-initialisation. Second, you (or somebody) mentioned that there
> might be optimisation benefits.
>
> Here’s a kind of partial initialisation you can use right now:
>
> public protocol SomeProtocol {
> var neverReallyMutatesAfterInit: String { get }
> }
>
> public extension SomeProtocol {
> static func withProtocolDefaults() -> SomeProtocol? {
> return (Self.self as? PartiallyInitializableSomeProtocol.Type).map
> { $0.withProtocolDefaults() }
> }
> }
>
> internal protocol PartiallyInitializableSomeProtocol: SomeProtocol {
>
> /// Creates an object with type-specific defaults.
> init()
>
> /// Allows customisation by the protocol after init.
> var neverReallyMutatesAfterInit: String { get set }
> }
>
> internal extension PartiallyInitializableSomeProtocol {
> static func withProtocolDefaults() -> SomeProtocol {
> var newValue = Self()
> newValue.neverReallyMutatesAfterInit = "ProtocolDefault"
> return newValue
> }
> }
>
> public struct SomeConformer: PartiallyInitializableSomeProtocol {
> public internal(set) var neverReallyMutatesAfterInit = "TypeDefault"
> internal init() {}
> }
>
> (SomeConformer.self as
> SomeProtocol.Type).withProtocolDefaults()?.neverReallyMutatesAfterInit //
> "ProtocolDefault"
>
>
> With this example, the only way users of your library can obtain an
> instance is via the protocol function, which knows that it can construct a
> default instance and specialise the values.
>
> As far as optimisation goes - I have to correct myself, because even in a
> protocol existential whose underlying type is known to have value
> semantics, computed properties themselves might not. For all the compiler
> knows, they could be reading/writing from global state. So the compiler
> can’t really eliminate repeat accesses unless it knows the underlying
> implementation (or we give it more information about how the content
> mutates). People have asked about marking stored/computed properties in
> protocols before, and I’ve always been against it. Protocols should contain
> abstract, semantic information with as little implementation-constraining
> stuff as possible whilst retaining the contract. That’s what makes them so
> powerful.
>
> - Karl
> _______________________________________________
> 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/20170627/ae0c4adc/attachment-0001.html>
More information about the swift-evolution
mailing list