[swift-evolution] Partially Constrained Protocols [Was: [Proposal] Separate protocols and interfaces]

David Waite david at alkaline-solutions.com
Mon Jan 25 16:58:39 CST 2016


> On Jan 25, 2016, at 2:52 PM, Maximilian Hünenberger <m.huenenberger at me.com> wrote:
> 
> Am 22.01.2016 um 21:41 schrieb David Waite <david at alkaline-solutions.com <mailto:david at alkaline-solutions.com>>:
>> Since today equatable mandates a defined, public == method, and I think the generic is always loses to that specificity. You don’t even need dynamic casting, because your generic version only gets called when there isn’t a non-generic version.
>> 
>> // in this case there could be a more general == function
>> func == <T: Equatable, U: Equatable>(x: T, y: U) -> Bool {
>>     return false
>> }
>> 
>> 1 == 1 // true
>> 1 == “1” // false
>> 
> 
> I thought also of this example:
> 
> let a: Equatable = 1
> let b: Equatable = 2
> 
> a == b // would fail in your case

I was speaking of today - today you can’t express "let a:Equatable = 1”.

>> 
>> In that, the covariant case, Array<Element:Number> is _different_ than Array<Number> today.
>> 
>> Today, Array<Number> and Array<Int> are different concrete types. One deals in terms of Numbers, and one in terms of Ints. They are structured in memory different, etc.
>> 
>> When I say partial constraints, applied to generics in this case (reluctantly), I’m saying the following:
>> 
>> Implicitly define a protocol around all concrete Array instances where Element is defined to be Number or a subtype of Number, exposing all methods, properties, etc which are safe.
>> 
>> Safety rules are equivalency rules here: If a method or property returns Element, I can say that it is safe to represent the result of “var first:Element {get}” as a Number, because in the cases where it returns a subtype like Int, I can still safely upcast to Number. I have a safe and uniform interface.
>> 
>> If a method takes an argument or for any property setter, I have to require Element to be invariant. Modifying the first item of an array isn’t safe, because I might try to assign an Int value when really its a UInt8 array.
> 
> Why Element has to be invariant?
> 
> let uintArray: [UInt8] = [1]
> var covariantArray: [Number] = uintArray
> // this doesn't cause an error since Int is also a Number
> covariantArray[0] = Int(36754)

A generic instance is still a concrete type, so for instance:
- Array<.Element == Int> is a concrete type, will store Int value types internally (e.g. sizeof(Int) == 8), and the API will allow you to subscript get and set Int values,etc
- Array<.Element == Any> is a concrete type, will store Any protocol references internally (e.g. sizeof(Any) == 32), and the API will allow you to subscript get and set Any values,etc
- a new Array<.Element:Any> is a protocol type, refers to/boxes any concrete Array without regards to its internal member (e.g. element size varies), and the API might allow you to subscript get Any because it can upcast all type instances to Any, but will hide the ability to set Any. The type system can’t be sure the concrete type you are supplying is appropriate for the actual Element type of the concrete Array (without changes in that feature as well, subscript must get and set, so the subscript get would also be hidden)

Per your proposed syntax, your set above would need to fail at compile-time or will fail at runtime, because covariantArray is a protocol boxing a copy of the [UInt8]-based uInt8Array, without additional behavior.

You could define a process where a Array<.Element == Number> is created by the compiler in that situation off of the uintArray value and assigned into covariantArray before the assignment happens, but thats a very expensive action to take implicitly, would require a dynamic check on each access. Plus, that would fail for generic types which were classes instead of value type (because you would only be updating a single reference to point to the new object)

<snip, to respond later>
> 
> PS: Have you made a formal proposal already?

Not yet, I was hoping to get more feedback on the value of the idea before writing up all of the rules, which will take many hours of my time (to fail fast). There has been very little discussion so far.

My hope is to have a discussion on where the generics and constrained protocol systems should go for Swift 3 and beyond, but I suspect the core team is occupied by Swift 2.2 and the API guidelines discussion ATM. Type system changes are IMHO something that really warrant having a roadmap extending several releases into the future, and there are a number of ones being proposed by both the core team and evolution members (evolutioneers?) in general.

-DW
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160125/66e87f27/attachment-0001.html>


More information about the swift-evolution mailing list