-
Notifications
You must be signed in to change notification settings - Fork 29
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
base: master
Are you sure you want to change the base?
Conversation
### 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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 :)
@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:
That's it :) Tell me if you need more info/examples, I'll be glad to add them |
Link to text: https://github.com/AdaCore/ada-spark-rfcs/blob/topic/final/considered/final_modifier.md