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

Open
wants to merge 8 commits into
base: main
Choose a base branch
from

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

class C extends D {} // C is a subtype AND a subclass of D.
```

Everything in Dart is an object, so _subtype_ is also used to describe
Copy link
Member

Choose a reason for hiding this comment

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

A few issues here:

  • There are many languages that openly embrace the "everything is an object" philosophy; I'm not clear on where we land on that - this may be a good question for language team folks. In any case, I think null safety has complicated this question a bit - Null is now a class which does not implement Object, and we even call attention to that in the docs. Of course null is an object (but not an Object) belonging to that class, so... maybe it's okay as long as "object" remains lowercase?
  • Having said all that, everything being an object is immaterial here. Subtyping is orthogonal to object-orientedness.
  • This mentions that "subtype" encompasses more relationships than simply extends/inherits, but this kinda feels like an afterthought even though it's fairly important IMO.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe the text here simply attempts to say that the 'subtype' relationship must include many other cases than MyClass implements OtherClass, because there is a subtype relationship between Object? (before null safety that would be Object), and hence the subtype relationship includes cases T1 <: T2 for all kinds of types T1 (because, at least, T1 <: Object?).

There might be some ways to say that which are more explicit and less confusing.

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

class C extends D {} // C is a subtype AND a subclass of D.
```

Everything in Dart is an object, so _subtype_ is also used to describe
Copy link
Member

Choose a reason for hiding this comment

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

Maybe the text here simply attempts to say that the 'subtype' relationship must include many other cases than MyClass implements OtherClass, because there is a subtype relationship between Object? (before null safety that would be Object), and hence the subtype relationship includes cases T1 <: T2 for all kinds of types T1 (because, at least, T1 <: Object?).

There might be some ways to say that which are more explicit and less confusing.

@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

Visit the preview URL for this PR (updated for commit 9502ff9):

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

@dart-lang dart-lang deleted a comment from github-actions bot Feb 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
review.await-update Awaiting Updates after Edits
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add Class modifiers, supporting definitions to glossary
7 participants