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

Douglas Gregor dgregor at apple.com
Thu Jan 14 00:20:06 CST 2016


> On Jan 13, 2016, at 9:07 PM, Howard Lovatt <howard.lovatt at gmail.com> wrote:
> 
> @Douglas,
> 
> Sure you can use SequenceType and constrain its associated types in where clauses. But it is hard work :(. When you write your own stuff you end up writing the equivalent of AnySequence and AnyGenerator as well, also hard work.
> 
> I am proposing something that reduces programmer burden and strikes a different balance,

That’s not what my comment was about :)

You brought up the way Java handles subtyping of arrays:

	There is a strong positive precedence for a type check on write, Java arrays (not Java `List`s). Arrays in Java may be passed covariantly, and this is extensively used. However if you attempt to write the wrong type into the array you will get an `ArrayStoreException`. In practice you don't get many `ArrayStoreException`, non of my code has ever had one. Its just not something you do in practice, as noted before contravariance is rare.

I’ve always considered that behavior to be a flaw in Java’s design, and the kind of thing that one typically relies on in a language that lacks generics. I’m not totally alone in this—the third hit Google returns when I search for “java type hole” is

	http://c2.com/cgi/wiki?JavaArraysBreakTypeSafety

Also, I don’t know to what extent your claim that subtyping of arrays is used extensively in Java is true. If you have data, I would *love* to see it.

Back to my actual point, Swift implements subtyping of arrays without introducing dynamic checks. Consider this code:

	class Base { }
	class Derived : Base { }

	var arrayOfDerived: [Derived] = [Derived(), Derived(), Derived()]
	var arrayOfBase: [Base] = arrayOfDerived
	arrayOfBase[0] = Base()

It compiles, it runs, and it doesn’t break because arrays have value semantics in Swift. The corresponding reference-semantic arrays (as in Java) require dynamic checking because the underlying array of Derived’s is being shared, so the assignment would fail with an ArrayStoreException. That’s not the case in Swift, because there’s no (logical) sharing between the array of Derived values and the array of Base values. For me, realizing that value semantics made subtyping of collections just work was a minor epiphany.

	- Doug

> 
>  -- Howard.
> 
> On 14 January 2016 at 15:38, Douglas Gregor <dgregor at apple.com <mailto:dgregor at apple.com>> wrote:
> 
> 
> Sent from my iPhone
> 
> On Jan 13, 2016, at 5:05 PM, Howard Lovatt via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
> 
>> @Robert,
>> 
>> The behaviour you show for Java arrays is what I am proposing, but that isn't type unsafe. The type error is detected at runtime and is flagged with an ArrayStoreException. This is unlike C for example that would allow the store, at least with a cast,  and would put into the array the address of the string. Therefore neither the proposal or Java is type unsafe, both are type safe and both detect type errors at runtime.
> 
> In case you hadn't noticed, Swift provides a basic form of covariance for the collections in the standard library... And it does so without the need for runtime checking, because value semantics allow safe mutation and collection covariance.
> 
>   - Doug
> 
> 
>> 
>> The question is whether protecting against this is worthwhile, not whether it can be protected against or not (it can be). Arrays are a good example, Swift takes the approach (like most languages) of checking the array size at runtime. But languages with dependent typing check the array size at compile time, i.e. a dependently typed language would detect this error:
>> 
>> let array = [1]
>> array[0] // 1, OK
>> array[1] // Error not detected by compiler but detected at runtime
>> 
>> because in such a language array would be typed as int array of size one, not int array of any size.
>> 
>> So the real question is in a language like Java, how many times do you get ArrayStoreException? In my own code I have never seen an array store exception! Why? Because contravariant writes are very rare. If you were to protect against contravariant writes you would be complicating the language for no practical gain, which I think is a bad trade off.
>> 
>> Hope this explains the reasoning,
>> 
>>  -- Howard.
>> 
>> On 14 January 2016 at 09:21, Developer <devteam.codafi at gmail.com <mailto:devteam.codafi at gmail.com>> wrote:
>> It does indeed make the type system unsound in some cases.  You have chosen one particular variance direction because it is convenient. A classic counterexample is Java making Arrays covariant by default.  So this works
>> 
>> Integer[] arr = [2, 4, 6, 8, 10];
>> Object[] orr = arr;
>> orr[0] = "crash bang";
>> 
>> And crashes at runtime.  For that, I must give this part of the proposal a strong -1.  Any amount of type safety I have to give up in the name of convenience is far too much.
>> 
>> I am, however, a fan of generic protocols.  They seem like an orthogonal concept given the first part here though.
>> 
>> ~Robert Widmann
>> 
>> 2016/01/13 17:03、Howard Lovatt via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> のメッセージ:
>> 
>>> @Thorsten,
>>> 
>>> It doesn't make the type system unsound; the types are mostly checked at compile time but some are runtime checked, either way the types are checked and therefore the type system is sound. I have posted an example of array runtime type checking in a previous response.
>>> 
>>> You can annotate variance, but this generally adds a lot of clutter to the code (see some typical Java code that uses generics you will see stuff like ? extends Type everywhere). In other languages like Scala the annotation is less visually intrusive, because they use shorter syntax and because it is annotated at declaration site rather than use site, but it is still there.
>>> 
>>> I think Swift arrays are a good example of partially static and partially runtime checked. The content is statically typed but the size is runtime typed. Other languages do type both the content and the size (see Dependent Typing on Wiki), however at some considerable burden on the programmer. 
>>> 
>>> Hope this explains the thought process,
>>> 
>>>  -- Howard.
>>> 
>>> On 13 January 2016 at 16:50, Thorsten Seitz <tseitz42 at icloud.com <mailto:tseitz42 at icloud.com>> wrote:
>>> Strong -1 from me as well for making the type system unsound.
>>> 
>>> > Am 13.01.2016 um 02:47 schrieb Howard Lovatt via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>>:
>>> >
>>> > Yes you can annotate for covariance, invariance, and contravariance, both Java and Scala, allow all three. The problem is that the code becomes splattered with variance annotations
>>> 
>>> Ceylon uses a different approach which is variance annotations at the definition site.
>>> This restricts the possible usage of the type parameters to appropriately variant positions.
>>> 
>>> This migt be a better way to deal with variance.
>>> 
>>> -Thorsten
>>> 
>>> 
>>> 
>>> -- 
>>>   -- Howard.
>>> 
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>> 
>> 
>> 
>> -- 
>>   -- Howard.
>> 
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-evolution <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/c14273e1/attachment.html>


More information about the swift-evolution mailing list