Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[RFC] Final modifier #95

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

[RFC] Final modifier #95

wants to merge 1 commit into from

Conversation

raph-amiard
Copy link
Member

@raph-amiard raph-amiard commented Nov 23, 2022

### Tagged types

Tagged types could be annotated with such a modifier, that would prevent
deriving from them outside of the package they have been defined in. This is
Copy link
Member

Choose a reason for hiding this comment

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

I would allow deriving from tagged types in child packages. This would enable the declaration of a hierarchy and still keep one tagged type per package (like I presented in this blog post https://blog.adacore.com/a-design-pattern-for-oop-in-ada).

package Widget is
   type Instance is tagged final null record;
end Widget;
package Widget.Button is
   subtype Parent is Widget.Instance;
   type Instance is new Parent with null record;
end Widget.Button;
package Widget.Button.Checkbox is
   subtype Parent is Button.Instance;
   type Instance is new Parent with null record;
end Widget.Button.Checkbox;

To completely lock the type hierarchy one would have to combine final on the tagged type and the package.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't see how this is possible: if you mark Widget as final, then you cannot declare Widget.Button and Widget.Button.Checkbox.

If you don't mark it as final, then nothing prevents somebody from the outside to derive the Widget package.

@Glacia
Copy link

Glacia commented Nov 24, 2022

The whole idea of having "final" tagged type seems wrong to me. Doesn't the word tagged implies it has a runtime tag? So with final keyword you're kinda trying to untag tagged type. Here is an idea: How about allowing regular record types to be derived within a package? Kinda like this:

package Game is
   Type Player_Base is 
      record
         Health : Positive := 100;
      end record;
   type Player_Mage is new Player_Base with -- Player_Mage has Health too!
      record
         Magic_Points :  Positive := 100;
      end record;
end Game;

Combined with https://github.com/AdaCore/ada-spark-rfcs/blob/master/considered/rfc-prefixed-untagged.rst you'll get pretty much the same thing as tagged final, no? Am i wrong?

> abstract methods and final methods, and make the user derive the object, are
> often done via generic packages with subprogram params in Ada.

### Library level packages
Copy link
Member

Choose a reason for hiding this comment

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

Isn't this already possible with private packages? Generally I'm not sure how useful that is. While I see the point with tagged types as they also carry the message "I'm an API you can derive from" implementing a child package is usually already a decision to intentionally access the internals of a package and nothing that happens "accidentally". So this mechanism doesn't really protect from accidental misuse.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not talking about people voluntarily creating a child package from another project.

When you have several people working on the same project, adding that annotation will serve as an indication to peers that the developer didn't intend this package to be derived. This annotation can be verified at compile time, which is much better than a comment.

There are two motivations for this feature:

* The possibility of annotating "finalness" of different entities to provide
better APIs that are more explicit/less easy to break.
Copy link
Member

Choose a reason for hiding this comment

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

This reinforces my earlier comment about final packages. IMO on a package level the API is already well defined through public and private parts of the spec. Creating a child package is an intentional decision to access private parts so there is no need to disallow it even more explicitly.

Copy link
Member Author

Choose a reason for hiding this comment

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

I disagree, for the same reasons cited above :)

@raph-amiard
Copy link
Member Author

@Glacia Sealing tagged types doesn't imply they don't have RTTI anymore, just that it cannot be extended from outside the package. You still have access to the tag, via dispatching primitives, or introspection.

You could see it as very similar to a discriminated record, where the discriminant is the tag. Actually the two are isomorphic. However, there are various reasons the second can be useful rather than the first:

  1. If you provide an API to outside users via a library, you might want internal developpers of your library to be able to derive from a base class and create new cases, potentially in different packages, but never allow outside users to derive from any of your classes. In that case, you can "seal" all the leafs of your derivation tree

  2. You might want to derive from an interface provided by a library, or even a base class. That doesn't mean that you want people to be able to derive from your implementation. In that case, you can seal it.

  3. For various reasons, today in Ada, tagged types might be a better fir for your API than a discriminated record (Libadalang is an example of that). You still don't want people to be able to derive from your hierarchy.

That's it :) Tell me if you need more info/examples, I'll be glad to add them

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants