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

Add chips to class pages for class modifiers #3401

Merged
merged 3 commits into from
Apr 27, 2023

Conversation

jcollins-g
Copy link
Contributor

@jcollins-g jcollins-g commented Apr 27, 2023

Fixes #3392.

This adds chips indicating the new class modifier features (as well as adding 'abstract') to each class page. Class modifiers and abstract are now removed from the breadcrumbs.

The implementation reuses parts of the old "language feature" system that we had used for displaying "Null safety" chips everywhere -- fortunately that infrastructure is still in place so we do not have to alter the templates. We should still rename and perhaps reorganize that system a bit: #3400.

Sample screenshot:
Screenshot 2023-04-27 at 8 46 42 AM

Copy link
Member

@srawlins srawlins left a comment

Choose a reason for hiding this comment

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

LGTM

const Map<String, String> _featureDescriptions = {
'sealed': 'All direct subtypes must be defined in the same library.',
'abstract': 'This type can not be directly constructed.',
'base': 'This type can only be extended (not implemented or mixed in).',
Copy link
Member

Choose a reason for hiding this comment

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

"... outside of the library in which it is declared"? ☹️

Maybe it's better to be wrong but concise?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure our language team friends would have phrased it like that, but yes, this shorter version is what I was told was going to be advertised.

@jcollins-g jcollins-g merged commit c02f0b5 into dart-lang:main Apr 27, 2023
@jcollins-g jcollins-g deleted the class-modifiers-chips branch April 27, 2023 19:49
const Map<String, String> _featureDescriptions = {
'sealed': 'All direct subtypes must be defined in the same library.',
'abstract': 'This type can not be directly constructed.',
'base': 'This type can only be extended (not implemented or mixed in).',
Copy link
Member

Choose a reason for hiding this comment

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

Probably in this context, better to say "This class can..." here, and in each of the ones below?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

base can apply to mixins too, but class or mixin isn't too long to say where it applies. Will do.

/// the user that the documentation should be specially interpreted.
const Map<String, String> _featureDescriptions = {
'sealed': 'All direct subtypes must be defined in the same library.',
'abstract': 'This type can not be directly constructed.',
Copy link
Member

Choose a reason for hiding this comment

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

I don't think "can not be directly constructed" is the most relevant bit here for users. Maybe "This class may have abstract members and cannot be directly constructed"?

/// An abstraction for a language feature; used to render tags to notify
/// the user that the documentation should be specially interpreted.
const Map<String, String> _featureDescriptions = {
'sealed': 'All direct subtypes must be defined in the same library.',
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 subtypes of this class will be checked for exhaustiveness in switches"? I'm assuming these need to be brief and self contains, and sealed means quite a bit: it really means "This class is abstract and all direct subtypes must be defined in the same library. Switches over the direct subtypes of this class will be checked for exhaustiveness". But that's probably too much for this? So barring that, I think exhaustiveness is the important bit for users.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The exhaustiveness bit is the most important part for authors of a class, but for users of a class wanting to know its API surface, the exhaustiveness checking it does internally is actually irrelevant AFAIK? Still, maybe making sure people know how to use sealed properly is so important that it trumps describing what's actually visible on the API surface. I'll put this new wording in for now and see how it lands with people.

Copy link
Member

Choose a reason for hiding this comment

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

I'm not 100% sure what qualifies as API surface here so I'm not sure quite how to classify this. Most class modifiers primarily affect what another class author can do with a class. That is, final says "other class authors are forbidden from extending or implementing this class". But it doesn't affect anything about how non-class authors use the class (that is, it does not affect the API of instances of this class at all). The sealed modifier affects both class-authors and non class-authors. That is, a class author might want to know that a sealed class can't be extended or implemented (outside of its library). But a non class-author will definitely want to know that "this class is intended to be the type of a closed family (an algebraic datatype), and hence it is likely intended to be switched over, and I will get exhaustiveness checking if I do so". Ideally, we'd surface both, but if we surface only one I believe it should be the latter. My reasoning is that if what you care about is "can't be subclassed or implemented outside of the library" just use final. It's every bit as good. The only reason to use sealed is that you want to provide clients of your API with exhaustiveness checking. And therefore, I'd argue that that is the most important thing to surface.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TL;DR: This sounds reasonable, I agree. If you care, see below for where I was coming from originally.

An extremely broad definition of the API surface of a class (or any declaration) is : What can I do with this class, using the Dart language, that does not require modifying the class itself? So that includes (indirectly) instances as well as class authors, as well as people modifying the library the class is contained in. It's been my belief that in an ideal universe, API documentation should make it clear what can be done with a thing without having to read its source code.

A more (IMHO, mileage may vary) useful, limited definition that covers most cases where people are looking at documentation of something: What can I do with this class that does not involve modifying the class itself or the library (or even, package) it came from? This includes instances as well as class authors like the above, but excludes the case where (for example) a Flutter application developer might be looking to modify Flutter itself to solve a problem. I figure, most of the time a Flutter application developer is not looking to modify Flutter itself as part of their app development but does want to know the behavior of Flutter classes, methods, and so on.

It was this second definition that led me to think that the other aspect of sealed (that it can not be subclassed or implemented outside of the library) was more important in API documentation. But your point about final makes a lot of sense to me. Since the major differences between sealed and final only make sense within the library itself, we probably should highlight that difference. So I'm now in agreement that your suggestion is best.

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.

Dart 3 class modifiers should be clearly visible in dartdocs
3 participants