Skip to content

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

Merged
merged 1 commit into from
Mar 29, 2017
Merged

Rewrote variances tour #741

merged 1 commit into from
Mar 29, 2017

Conversation

mhzajac
Copy link

@mhzajac mhzajac commented Mar 21, 2017

Split different types of variances into separate sections,
and replaced the complicated Stack example with simpler
ones.

@mhzajac
Copy link
Author

mhzajac commented Mar 21, 2017

#733

@@ -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.
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Member

@SethTisue SethTisue Mar 21, 2017

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.
Copy link
Contributor

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? .

Copy link
Member

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]`.
Copy link
Contributor

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)
Copy link
Contributor

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?

Copy link
Author

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)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe a multiline comment?

Copy link
Contributor

@travissarles travissarles left a 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]`.
Copy link
Contributor

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.

Copy link
Contributor

@julienrf julienrf left a 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 like F[T] (and use the same for the definition of contravariance). Or, you could stick with List that you use below in your example.

@mhzajac
Copy link
Author

mhzajac commented Mar 21, 2017

Thanks for your feedback.

@julienrf I removed that part because it referenced the complicated Stack example, but perhaps that was too much. Looking at it again I think mentioning something like the original sentence "This can restrict the reuse of the class abstraction." is important. Is that the one you had in mind?

Also, naming this is hard. I originally called Request[T] MyClass[T], instead, but was worried about using something that might seem cryptic to the first-time reader.

@travis032654 I stuck with T as it may come as more familiar to those coming from languages that seem favor it with generics. Happy to change it though. I'll make the updates later today.

@tpolecat
Copy link

I have found that for a lot of people the key type to look at is Function1 which is contravariant on the left and covariant on the right. If you get this one then the others are easy. So I think a section on this might be helpful. The discussion goes something like:

If I ask you for a Monkey => Fruit and you give me an Animal => Apple it will work fine: it still takes a monkey (because it can take any animal) and it still returns a fruit (because an apple is a fruit). This makes intuitive sense, and is a formal relationship in Scala: Animal => Apple is a subtype of Monkey => Fruit.

Java programmers may recognize this relationship because it is reflected in Java's rules for overridden methods, which can be more general in argument types and more specific in return types.

@tpolecat
Copy link

tpolecat commented Mar 21, 2017

I agree that [T] should be [A] everywhere.

@SethTisue
Copy link
Member

the key type to look at is Function1

+1, this is also exactly what I reach for every time I try to explain this

@tpolecat
Copy link

Minor thing, but subtype and subclass should not be hyphenated.

@tpolecat
Copy link

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.
@mhzajac
Copy link
Author

mhzajac commented Mar 22, 2017

Updated based on everyone's feedback. I still don't like the first section all that much.

@SethTisue
Copy link
Member

SethTisue commented Mar 29, 2017

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!

@SethTisue SethTisue merged commit ca8cbd7 into scala:master Mar 29, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants