-
Notifications
You must be signed in to change notification settings - Fork 1k
Rewrote variances tour #741
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@@ -10,31 +10,114 @@ next-page: upper-type-bounds | |||
previous-page: generic-classes | |||
--- | |||
|
|||
Scala supports variance annotations of type parameters of [generic classes](generic-classes.html). In contrast to Java 5 (aka. [JDK 1.5](http://java.sun.com/j2se/1.5/)), variance annotations may be added when a class abstraction is defined, whereas in Java 5, variance annotations are given by clients when a class abstraction is used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we start this article with a sentence about what variances are? "Variances are ____". I'm not sure if it's common knowledge. Also, maybe we should just say Java instead of Java 5?
@@ -10,31 +10,114 @@ next-page: upper-type-bounds | |||
previous-page: generic-classes | |||
--- | |||
|
|||
Scala supports variance annotations of type parameters of [generic classes](generic-classes.html). In contrast to Java 5 (aka. [JDK 1.5](http://java.sun.com/j2se/1.5/)), variance annotations may be added when a class abstraction is defined, whereas in Java 5, variance annotations are given by clients when a class abstraction is used. | |||
Scala supports variance annotations of type parameters of [generic classes](generic-classes.html) similar to C#. In contrast to Java 5 (aka. JDK 1.5), variance annotations may be added when a class abstraction is defined, whereas in Java 5, variance annotations are given by clients when a class abstraction is used. | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be good to jump into a simple example here. I like to see code right away.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest moving the comparison to Java and C# to a distinct section which comes at the end, not at the beginning.
In the page about [generic classes](generic-classes.html) an example for a mutable stack was given. We explained that the type defined by the class `Stack[T]` is subject to invariant subtyping regarding the type parameter. This can restrict the reuse of the class abstraction. We now derive a functional (i.e. immutable) implementation for stacks which does not have this restriction. Please note that this is an advanced example which combines the use of [polymorphic methods](polymorphic-methods.html), [lower type bounds](lower-type-bounds.html), and covariant type parameter annotations in a non-trivial fashion. Furthermore we make use of [inner classes](inner-classes.html) to chain the stack elements without explicit links. | ||
### Covariance | ||
|
||
A type parameter `T` of a generic class can be made covariant by using the annotation `+T`. For some class `Request[T]`, making `T` covariant implies that for two types `A` and `B` where `A` is a sub-type of `B`, then `Request[A]` is a sub-type of `Request[B]`. This allows us to make very useful and intuitive sub-typing relationships using generics. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we say class Request[+T]
so we show from the signature that it's covariant? .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
case class Dog(name: String) extends Animal | ||
``` | ||
|
||
Both `Cat` and `Dog` are sub-types of `Animal`. The Scala standard library has a generic immutable `List[T]` class, where the type parameter `T` is covariant. This means that a `List[Cat]` is a `List[Animal]` and a `List[Dog]` is also a `List[Animal]`. Intuitively, it makes sense that a list of cats and a list of dogs are each lists of animals, and you should be able to substitute either of them for a `List[Animal]`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, I think it could be helpful to have the signature of List: sealed abstract class List[+A]
so we can see in the API that it's covariant.
val cats: List[Cat] = List(Cat("Whiskers"), Cat("Tom")) | ||
val dogs: List[Dog] = List(Dog("Fido"), Dog("Rex")) | ||
|
||
printAnimalNames(cats) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you put the output in a comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it prints out multiple lines, what might look acceptable?
Something like this?
// prints:
// Whiskers
// Tom
printAnimalNames(cats)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a multiline comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great explanation of a somewhat complicated topic. I think the examples are helpful. One minor style note: Should we use A instead of T for a type placeholder to be consistent with the API docs?
|
||
### Contravariance | ||
|
||
A type parameter `T` of a generic class can be made contravariant by using the annotation `-T`. This creates a sub-typing relationship between the class and its type parameter that is similar, but opposite to what we get with covariance. That is, for some class `Writer[T]`, making `T` contravariant implies that for two types `A` and `B` where `A` is a sub-type of `B`, `Writer[B]` is a sub-type of `Writer[A]`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be clearer to have the full class signature class Writer[-T]
to show what it looks like.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, thanks for working on that!
I really like it overall :)
I’m not super happy with the beginning, though:
- you removed one sentence that was (imho) giving insightful background information about the problem solved by variance annotations
- why using
Request[T]
at the beginning of the covariance section, and then using a completely different context? Since this is a general definition we could maybe use an abstract name likeF[T]
(and use the same for the definition of contravariance). Or, you could stick withList
that you use below in your example.
Thanks for your feedback. @julienrf I removed that part because it referenced the complicated Also, naming this is hard. I originally called @travis032654 I stuck with |
I have found that for a lot of people the key type to look at is
|
I agree that |
+1, this is also exactly what I reach for every time I try to explain this |
Minor thing, but subtype and subclass should not be hyphenated. |
This is a big improvement, thanks for your work on this! |
Split different types of variances into separate sections, and replaced the complicated `Stack` example with simpler ones.
Updated based on everyone's feedback. I still don't like the first section all that much. |
I agree that the first section would benefit from simpler language, but that can always happen in some future revision. I'm going to go ahead and hit "merge" anyway since this is already a huge improvement overall. Thank you for this! |
Split different types of variances into separate sections,
and replaced the complicated
Stack
example with simplerones.