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

decide exactly what should be prohibited for final classes #813

Open
zygoloid opened this issue Sep 3, 2021 · 7 comments
Open

decide exactly what should be prohibited for final classes #813

zygoloid opened this issue Sep 3, 2021 · 7 comments
Labels
leads question A question for the leads team

Comments

@zygoloid
Copy link
Contributor

zygoloid commented Sep 3, 2021

We should have a clear idea of exactly what should be allowed or prohibited for final classes -- exactly which rights are we reserving for class authors?

Some relevant considerations:

  • We might want to reserve the right for a class author to assume that anything that matches FinalClass* actually points to an instance of the class and not some other type. However, if we permit overloading of implicit conversion, then a user of the class may be able to define other types that implicitly convert to FinalClass* other than via inheritance.
  • We might want to reserve the permission for the interface of a class to be inherited into another type and then potentially modified in some way. However, adapter X extends FinalClass has this capability just like class X extends FinalClass and it's important that we can adapt any type.
  • We might want to reserve the right for types to access the protected interface of the class, or to change the implementation of the virtual interface of the class. However, (a) in general we want an ACL for this (eg: only these five classes can implement my virtual interface), and (b) preventing access to the protected interface or implementation of the virtual interface does not require that we prevent inheritance.
  • We might want to prevent casts from FinalClass* to a pointer to some enclosing (derived) type. However, we will probably want some general form of this facility (not related to inheritance) as an opt-in facility, since being able to navigate from a pointer to a subobject to a pointer to an enclosing object is a valuable operation.

We should be clear on which restrictions we want to enforce and should either apply them consistently or have rationale for why different cases are being handled differently.

@github-actions

This comment was marked as outdated.

@github-actions github-actions bot added the inactive Issues and PRs which have been inactive for at least 90 days. label Dec 3, 2021
@josh11b josh11b added leads question A question for the leads team and removed inactive Issues and PRs which have been inactive for at least 90 days. labels Nov 30, 2022
@josh11b
Copy link
Contributor

josh11b commented Nov 30, 2022

We've been saying one of the important things about final classes is that you can add names to them without evolution concerns. We could accomplish that by saying adapter X extends FinalClass is allowed, but can't add any names, just external impls. This allows the main use case for extending adapters, adding an implementation for an interface FinalClass did not know about, which seems like something we need. Presumably, in this case we would make X final as well.

@chandlerc
Copy link
Contributor

For myself, the two things I want is for the API and the virtual dispatch to be finalized and thus no longer open to extension. I think that means two key restrictions:

  • The one @josh11b mentions: no new member names can be added by users, so the type author can evolve them freely.
    • I'd suggest that, however we spell the "extending adapter" use case, we don't allow modifications to the API but preserving it exactly as-is and adding new external impls.
  • No new implementations of virtual methods, so calls to a virtual method through FinalClass* are directly dispatchable.

At least initially, I'm not seeing reasons to restrict pointer casts or matching.

@josh11b
Copy link
Contributor

josh11b commented Apr 5, 2023

Alternatively, we can add names in an extending adapter without evolution concerns if names in the adapter shadow those in the final class.

@josh11b
Copy link
Contributor

josh11b commented Apr 5, 2023

Of the possible rights reserved for final classes, my opinion is:

  • The implementation of virtual methods should be fixed. This supports the performance goal by avoiding virtual dispatch.
  • final classes should be allowed to be evolved without concerns about name collisions it is unaware of.

I haven't heard an argument for other restrictions that I've found compelling.

@josh11b
Copy link
Contributor

josh11b commented Apr 5, 2023

In particular, I'd like to advocate for a practical rather than philosophical principle here. We should reserve rights for final classes when we have a reason that furthers Carbon's goals, such as the performance and evolution goals used to justify the rights in my previous post.

@chandlerc
Copy link
Contributor

chandlerc commented Apr 7, 2023

Thinking about this more, I've come around to a slightly more restrictive stance here.

I think the implicit conversion afforded by deriving is in and of itself surprising for a final class. So the Carbon goal I'm anchoring on here is easy to read and understand.

The implicit conversion of inheritance is motivated by the general substitutable approach. For example, given a pointer to a final class T, having the pointed-to object not be of type T but some derived type, even with all of these restrictions, would significantly harm readability and understandability of the code.

A different way of thinking about this: I think a final class should now allow anything like inheritance because the very way we will likely want to teach final is precluding further inheritance.

However, I think the things we want to work with any type should also apply to final classes, including adapters.

So I think what we want to prohibit for final classes are:

  • Things involving inheritance, or modeled on inheritance -- it would actively confuse teaching and understanding the design of the code.
  • Things that aren't designed to work with all types -- which ensures an acceptable evolutionary impact and story to support that.

I'm not sure it makes sense to try to enumerate the second bullet point here, as we can't really know the full set of this. So is the concrete restriction in the first bullet point and the general direction of the second OK as an answer here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
leads question A question for the leads team
Projects
None yet
Development

No branches or pull requests

3 participants