[swift-evolution] Make generics covariant and add generics to protocols

Howard Lovatt howard.lovatt at gmail.com
Tue Jan 12 19:49:33 CST 2016


Would the optimisation I suggested in previous email address your concern
of needing both associated types and generics in protocols?

  -- Howard.

On 13 January 2016 at 08:31, Austin Zheng via swift-evolution <
swift-evolution at swift.org> wrote:

> AFAIK traits in Rust can have both associated types and generic type
> parameters. I think a corresponding feature for Swift would be a net
> positive, albeit I haven't examined the tradeoffs and advantages in any
> detail yet.
>
> Austin
>
> On Tue, Jan 12, 2016 at 1:16 PM, Maximilian Hünenberger <
> m.huenenberger at me.com> wrote:
>
>> 1+ for adding generics to protocols.
>>
>> What about generics in protocols which are only a view to its associated
>> types or generics which create/are associated types?
>>
>> Example of a simple protocol which models a node of a tree:
>>
>> // Before
>>
>> // NodeType can be anything
>> // currently Swift doesn't allow
>> // `typealias NodeType: Node`
>> //
>> // or even where clauses
>> // `typealias NodeType: Node where NodeType.T == T`
>> protocol Node {
>> typealias T
>> typealias NodeType
>>
>>
>> var value: T { get }
>> var nodes: [NodeType] { get }
>> }
>>
>> // After
>> protocol Node<T> {
>> typealias T // probably remove this declaration
>> var value: T { get }
>> var nodes: [Node<T>] { get }
>> }
>>
>> So a generic parameter is placed after the protocol name. Therefore a
>> corresponding associated type could be synthesized making its declaration
>> in the body of the protocol unnecessary.
>>
>> In order to let
>>
>> func afunction<S: SequenceType where S.Generator.Element == Int>(s: S){}
>>
>> still compile there could be a general Swift feature to get the generic
>> type by dot syntax (e.g. synthesized typealiases for every generic
>> parameter).
>>
>> The function declaration above could be rewritten to using function like
>> parameter syntax:
>>
>>     func afunction(s: SequenceType<Generator: GeneratorType<Int>,
>> SubSequence: _>){}
>>     // or omitting `SubSequence: _` since the type is already unambiguous
>>     func afunction(s: SequenceType<Generator: GeneratorType<Int>>){}
>>
>> in this case there is almost no win. But as you can see in the example
>> with the protocol, generics allow for much better abstraction.
>>
>> Also where clauses could be used in generic parameter declarations which
>> are disallowed for associated types.
>>
>>
>> Maximilian
>>
>> Am 12.01.2016 um 19:19 schrieb Jordan Rose via swift-evolution <
>> swift-evolution at swift.org>:
>>
>> Agreed on both counts. Generics are more familiar but don't actually
>> cover the use cases where the associated type is *not* independent of
>> the model object (like a Sequence's Generator or a Collection's Index).
>> Dropping that information results in a lot of extra indirection at runtime,
>> which we don't want.
>>
>> Jordan
>>
>>
>> On Jan 12, 2016, at 8:17, Austin Zheng via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>> Strong -1, covariance on generics should be explicitly opt-in. Also -1 on
>> generics replacing associated types in protocols.
>>
>> Austin
>>
>> On Jan 12, 2016, at 1:45 AM, Howard Lovatt via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>> Currently you generics are invariant whereas function arguments etc. are
>> covariant. I am suggesting that if the way generics are implemented is
>> changed then they can be made covariant and that this will add considerable
>> utility to Swift generics.
>>
>> 1st a demonstration of the current situation of invariant generics:
>>
>>     // Current system
>>     class Top {}
>>     class Bottom: Top {}
>>
>>     struct Box<T: AnyObject> {
>>         var value: T
>>         init(_ initialValue: T) {
>>             value = initialValue;
>>         }
>>     }
>>
>>     let boxB = Box(Bottom())
>>     // let boxT: Box<Top> = boxB // Covariance currently not allowed
>>
>> The key point is although `Bottom` 'is a’ `Top`, `Box<Bottom>` *is not* a
>> `Box<Top>`.
>>
>> I am suggesting:
>>
>> 1. That `Box<Bottom>` should be a `Box<Top>` (covariance).
>> 2. An implementation that allows the above covariance.
>> 3. That protocols are made generic, i.e. `protocol Box<T> { var value: T
>> { get set } }` and that this mechanism replaces associated types for
>> protocols.
>>
>>     // Proposal:
>>     // 1. No change to Box, i.e. programmer would just write Box as
>> before
>>     // 2. Code transformed by comiler with write check for each
>> specific, generic type instance
>>     // Best approximation of resulting code in current Swift to
>> demonstrate spirit of idea:
>>
>>     // Compiler writes a universal form using the upper bound (it writes
>> the underlyting representation).
>>     // In practice this would be called `Box` but used `BoxAnyObject` to
>> indicate that it has a generic argument bounded by `AnyObject`.
>>     struct BoxAnyObject {
>>         // Generated from generic argument `<T: AnyObject>`.
>>         let T: AnyObject.Type // Store the actual type.
>>
>>         // Generated from stored property `var value: T` and noting that
>> `T`'s upper bound is `AnyObject`.
>>         private var _value: AnyObject // Access the stored property
>> through a setter so that type can be checked
>>         var value: AnyObject {
>>             get {
>>                 return _value
>>             }
>>             set {
>>                 // In all functions check that args declared as `T` are
>> actually a `T` or a sub-type.
>>                 // Note: `is` only works with type literal and there is
>> no `>=` operator for types :(.
>>                 // `is` would need changing or `>=` for types adding,
>> nearest at moment `==`.
>>                 precondition(T == /* >= */ newValue.dynamicType, "Type
>> of newValue, \(newValue.dynamicType), is not a sub-type of generic type
>> T, \(T)")
>>                 _value = newValue
>>             }
>>         }
>>
>>         // Generated from `init(_ initialValue: T)` and noting that
>> `T`'s upper bound is `AnyObject`.
>>         init(_ lowestCommonDeclaredT: AnyObject.Type, _ initialValue:
>> AnyObject) {
>>             T = lowestCommonDeclaredT
>>             _value = initialValue
>>         }
>>     }
>>
>>     // Demonstrate that all `Box`es are the same size and therefore can
>> be bitwise copied
>>     // Compiler supplies lowest-common, declared, generic type for all
>> the `T`s in the `init` call.
>>     var bT = BoxAnyObject(Top.self, Top()) // In practice user would
>> write `let bT = Box(Top())`.
>>     bT.T // Top.Type
>>     sizeofValue(bT) // 16
>>
>>     var bB = BoxAnyObject(Bottom.self, Bottom()) // In practice user
>> would write `let bB = Box(Bottom())`.
>>     bB.T // Bottom.Type
>>     sizeofValue(bB) // 16
>>
>>     // Demonstration covariance.
>>     bT = bB // Compiler would check covariance of declared generic types.
>>     bT.T // Bottom.Type
>>
>>     // Demonstrate generic returned type
>>     // Compiler would add cast to declared, generic type.
>>     bB.value as! Bottom // In practice user would write `bB.value`.
>>
>>     // Demonstrate type safety
>>     bT = BoxAnyObject(Top.self, Top()) // In practice user would write
>> `bT = Box(Top())`.
>>     bT.value = Top() // OK
>>     // bT.value = Bottom() // Doesn't work at present because need `>=`
>> for types, but would work in practice
>>     // bB.value = Top() // Runtime error - wrong type
>>
>> The implications of this proposal are:
>>
>> 1. The compiler can statically type check a read from a stored property.
>> 2. A write to a stored property is type checked at runtime.
>> 3. Protocols can be made generic instead of having an associated type and
>> then they become a proper type with dynamic dispatch.
>> 4. Generic protocols can be a type just like non-generic protocols,
>> structs, and classes and unlike associated type protocols that can only be
>> a generic constraint.
>> 5. The awkwardness of dealing with associated type generics is replaced
>> by a more powerful and easier to understand semantic of a type, just like
>> the other types.
>> 6. There is a lot of ‘non-obvoius’, long code, for example `inits`, that
>> use a `where` clause to constrain an associated type protocol, this would
>> be unnecessary.
>> 7. There are whole types, `AnySequence`, `AnyGenerator`, etc., that would
>> be replaced by a generic protocols, `Sequence`, `Generator`, etc.
>>
>> Advantages:
>>
>> 1. Covariant generics are a powerful addition to the language.
>> 2. Generics’ invariance are inconsistent with the rest of the language.
>> 3. Generic protocols would become a ‘proper’ type and you could have
>> arrays and fields of a generic protocol.
>> 4. There are many threads on swift-evolution looking at how protocols can
>> be made into a ‘proper’ type or at least a concept that is easier to
>> understand.
>>
>> Compatibility:
>>
>> 1. This would be a major change since associated types in protocols would
>> be replaced by generics.
>> 2. The new implementation of generics might break some existing `struct`
>> and `class` code, for example if it is dependent on the exact size of an
>> object because the class will have extra fields, one for each generic type,
>> and therefore will be larger.
>>
>> Disadvantages:
>>
>> 1. Major change.
>> 2. Object size increases.
>>
>> Thanks in advance for any comments,
>>
>>   — Howard.
>>
>> PS This is part of a collection of proposals previously presented as
>> “Protocols on Steroids”.
>>
>> _______________________________________________
>> 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
>>
>>
>>  _______________________________________________
>> 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
>
>


-- 
  -- Howard.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160113/9f86c33d/attachment.html>


More information about the swift-evolution mailing list