Skip to content
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

Glossary entries for "Subtype" and "Subclass" #4958

Merged
merged 13 commits into from
Jun 7, 2024
Merged

Conversation

MaryaBelanger
Copy link
Contributor

@MaryaBelanger MaryaBelanger commented Jun 5, 2023

Fixes #4784

Sparked from this comment.

Yes - there are many subtype relations which are not subclass relations. If we're talking about classes, it's essentially the difference between implements and extends.

If class Foo implements Bar, then Foo is a subtype of Bar, but not a subclass. If class Foo extends Bar, then Foo is both a subtype and a subclass of Bar.

I tried to explain the difference while still having them make sense as stand alone glossary entries. I'm not really sure I captured the nuance. @fishythefish since you gave the preliminary explanation, and @johnpryan since you asked the question, would you mind evaluating this? Are these definitions useful?

Based on the definitions I added in this PR, I'm now unsure about some of the uses of subclass vs. subtype in the main Class Modifiers page. E.g. This use of "subclasses" under final seems like it could be incorrect?:

The final modifier encompasses the effects of base, and therefore any subclasses must also be marked base, final, or sealed.

  • Either because final prevents both implements and extends, so it should be "...any subtypes..."?
  • Or, is it deliberate because maybe only subclasses (declared with extends on the final superclass) need to be marked base, final or interface, but not subtypes (declared with implements on the final superclass)?

I'm probably overthinking it; any thoughts are appreciated!

(Ignore the empty glossary page, we're working on filling it out and standardizing its use.)

ToDo:

  • If approved, add links to these definitions from class-modifiers.md

@MaryaBelanger MaryaBelanger changed the title Subtype/subclass Glossary entries for "Subtype" and "Subclass" Jun 5, 2023
Copy link
Member

@fishythefish fishythefish left a comment

Choose a reason for hiding this comment

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

I may not be the right person to review this - maybe @leafpetersen?

As a general comment, we often talk about subtyping and subclassing as if the latter were a special case of the former, but I think this is a bit of a category mistake. We often get away with abusing the terminology a little, but we may want to be more precise here. (Part of the reason for this abuse is that "subclassing" is often used as a synonym for "implementation inheritance" and "subtyping" is used as a synonym for "interface inheritance" rather than subtyping in general.)

Strictly speaking, we have types and we have classes; subtyping is a relation on the former, and subclassing is a relation on the latter. We often refer to a type and a class by the same name, which can be convenient but also leads to conflating the two.

Not every subtyping relationship has to do with subclassing - for example, int is a subtype of int?, and String Function() is a subtype of void Function().

The converse is a bit subtler. If I declare a class Foo, I have also implicitly defined an associated type Foo which instances of the class Foo inhabit. These are two different logical entities, but there's a clear mapping from the class to the type, so in this sense, a subclass relationship also implies an associated subtype relationship. But we need to take care around generics: if class Foo<T> extends Bar<T>, then Foo is a subclass of Bar (do we need to include the type arguments here? I'm not sure) but not every instantiation of Foo is a subtype of every instantiation of Bar.

src/resources/glossary.md Outdated Show resolved Hide resolved

## Subtype

A _subtype_ is a class that implements another class
Copy link
Member

Choose a reason for hiding this comment

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

This is too narrow a definition. Many subtype relationships have nothing to do with classes. For example, T is a subtype of T? (for any type T).

Copy link
Member

Choose a reason for hiding this comment

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

Agreed. I think it makes sense to rely on the subtype relationship: Whenever we have T1 <: T2 according to the subtype rules, T1 is a subtype of T2. class MyClass implements OtherClass ... is just one special case.

Copy link
Member

Choose a reason for hiding this comment

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

Let me just link the subtyping spec here for visibility. We could link to that from the glossary, but I don't know if we want to limit how technical these explanations are.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks @fishythefish that's helpful. We try not to link directly to the specs (like you said, too technical for general use) but it'll help my understanding a lot!

Copy link
Member

Choose a reason for hiding this comment

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

One approach is to talk about this semantically rather than syntactically. That is, if I were writing something introductory on subtyping I'd say something like this: "A subtype is a type which is fully substitutable for another type. That is, it supports all of the operations of the supertype (along with possibly some extra operations). For example, if I have a type Animal, then the type Cat might be reasonably be a subtype of Animal since all Cats are Animals (but not vice versa). In practice, this means that a value of a subtype can be assigned to any location expecting the supertype, and all of the methods of the supertype are available on the subtype. Subtypes can be introduced for classes via implementation or inheritance. Other types, such as function types and nullable types, have structural subtyping: that is, they are judged to be subtypes based solely on the structure of the type. So for example, for any nullable type T?, we say that T is a subtype of T?, based just on the structure of the type. "

src/resources/glossary.md Outdated Show resolved Hide resolved
@eernstg
Copy link
Member

eernstg commented Jun 6, 2023

I'm not so worried about the category mismatch: In every situation where we wish to topicalize implementation inheritance (that is, we're considering class B extends A {...} and the question is "how come myA.foo() runs some code in the body of B?"), it is most likely implied by the context that we are talking about inherited implementations, and hence about classes and subclass relationships.

With that in mind, I tend to think that it's OK to categorize subtype relationships into (1) "subclass" relationships, established by extends S with M1 .. Mk (or, as a special case extends S), and (2) every other relationship which is included in the subtype relation (so Object? <: dynamic even though none of them is a class, and int Function() is a subtype of num Function() even though there is no syntax in the program specifying any such relationship, but also MyMixin on SomeClassOrMixin and MyClass implements SomeOtherType, and the relationship between a type variable X and and its bound, e.g., between two type variables X and Y when X is declared as X extends Y).

If we wish to target subtype relationships that are specifically created by an implements clause (as in MyClass implements SomeOtherType), we will probably have to talk about the syntax explicitly (saying things like "assume class C implement B {}", or at least "assume that class C implements class B").

I added a couple of comments in the files, too.

Copy link
Member

@eernstg eernstg left a comment

Choose a reason for hiding this comment

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

It looks fine to me, but I'm just adding a comment — such that I don't create any kind of unwanted interference.


## Subtype

A _subtype_ is a class that implements another class
Copy link
Member

Choose a reason for hiding this comment

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

Agreed. I think it makes sense to rely on the subtype relationship: Whenever we have T1 <: T2 according to the subtype rules, T1 is a subtype of T2. class MyClass implements OtherClass ... is just one special case.

src/resources/glossary.md Outdated Show resolved Hide resolved
@fishythefish
Copy link
Member

Agreed - I'm generally not too worried about the category mismatch either, and the intended meaning has been clear in almost every conversation that I've had.

Maybe another way to phrase my concerns is that there are essentially two audiences: those wondering about subtyping generally (or using this as a reference to e.g. read a feature specification), and those wondering about interface inheritance specifically (especially if they're coming from another (object-oriented) language, e.g. Java).

IMO, since this is a general-purpose glossary for Dart, we should mainly target the former. I think it would be okay to then mention that implements (and extends, with, etc.) gives us one special case of subtyping. I would just be careful about implying that this kind of subtyping is first-class and every other subtyping relationship is some kind of side effect.

@MaryaBelanger
Copy link
Contributor Author

IMO, since this is a general-purpose glossary for Dart, we should mainly target the former. I think it would be okay to then mention that implements (and extends, with, etc.) gives us one special case of subtyping.

Great insight, I was operating under the impression that implements was the point of "subtype" myself.

I think that we could mention implements and link to its language tour content, and maybe mention there that it creates a subtype relationship, and any other elaboration needed for that one case.

@MaryaBelanger MaryaBelanger removed the request for review from johnpryan June 15, 2023 19:12
@atsansone atsansone added the review.tech Awaiting Technical Review label Jul 10, 2023
@MaryaBelanger MaryaBelanger self-assigned this Dec 1, 2023
@MaryaBelanger MaryaBelanger added review.await-update Awaiting Updates after Edits and removed review.tech Awaiting Technical Review labels Dec 1, 2023
@dart-github-bot
Copy link
Collaborator

dart-github-bot commented Feb 11, 2024

Visit the preview URL for this PR (updated for commit 344c828):

https://dart-dev--pr4958-subtype-subclass-iwh6vcvk.web.app

@dart-lang dart-lang deleted a comment from github-actions bot Feb 11, 2024
Copy link
Member

@eernstg eernstg left a comment

Choose a reason for hiding this comment

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

Tricky stuff! ;-) I added a couple of comments.

src/content/resources/glossary.md Show resolved Hide resolved
src/content/resources/glossary.md Outdated Show resolved Hide resolved
Copy link
Member

@eernstg eernstg left a comment

Choose a reason for hiding this comment

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

I think this one has been waiting for quite a while, and there were a lot of good discussion about it. So I'll approve, just in case it is helpful.

@MaryaBelanger
Copy link
Contributor Author

@eernstg I'm definitely still going to go through and incorporate the comments before merging, but thanks for the approval! Appreciate all the explanations here, tricky stuff indeed :)

@MaryaBelanger
Copy link
Contributor Author

MaryaBelanger commented Jun 4, 2024

@fishythefish @leafpetersen @eernstg I rewrote both sections, taken basically word for word from the comments here (thank you all!)

If anyone wants to take another look that would be greatly appreciated!

Here are the staged sections: https://dart-dev--pr4958-subtype-subclass-iwh6vcvk.web.app/resources/glossary#subclass

src/content/resources/glossary.md Outdated Show resolved Hide resolved
src/content/resources/glossary.md Outdated Show resolved Hide resolved
src/content/resources/glossary.md Outdated Show resolved Hide resolved
src/content/resources/glossary.md Show resolved Hide resolved
src/content/resources/glossary.md Outdated Show resolved Hide resolved

This is true at least statically.
A specific API might not allow the substitution at run time,
depending on its operations.
Copy link
Member

Choose a reason for hiding this comment

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

If it helps to have a specific example:

void f(List<num> l) {
  l.add(3.14);
}

List<int> is a subtype of List<num> (this might be a good place to link to this FAQ or similar documentation), so f(<int>[1, 2, 3]) passes static checks, but fails at runtime. f(<num>[1, 2, 3]) works.

@MaryaBelanger MaryaBelanger added review.copy Awaiting Copy Review and removed review.await-update Awaiting Updates after Edits labels Jun 7, 2024
Copy link
Contributor

@atsansone atsansone left a comment

Choose a reason for hiding this comment

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

Would ask that you check for active voice, shorter sentences and condition-action before merging, but that's non-blocking

@MaryaBelanger MaryaBelanger merged commit 23fd8ec into main Jun 7, 2024
10 checks passed
@MaryaBelanger MaryaBelanger deleted the subtype/subclass branch June 7, 2024 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
review.copy Awaiting Copy Review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Class modifiers, supporting definitions to glossary
7 participants