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@swift.org> wrote: > > On 26. Jun 2017, at 13:44, Karl Wagner <razie...@gmail.com> wrote: > > > On 25. Jun 2017, at 22:13, Robert Bennett <rltbenn...@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@swift.org > https://lists.swift.org/mailman/listinfo/swift-evolution >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution