My dear friend,
I am unconsolable about your desparation. I must say though that I cannot fully understand your confusion - have you forgotten my basic principle to bequeath only what is actually visible? In order to give you a helping hand, we'll run together step by step through my and your program. You write you have done the same as I. In the end you'll hopefully see that what you've done is only seemingly the same.
The type Root is not visibly derived from Ada.Finalization.Controlled, so that the operations in the private part of Mystery_Monger are inheritable only where they are also visible, i.e. in child packages.
Now Derived is a child of Mystery_Monger and thus Something inherits and overrides the primitive operations Initialize, Adjust, Finalize.
In the program Try_It everything happens therefore as described. It doesn't matter that the inherited operations have been (invisibly) overridden in the private part.
Now let's have a look at your program. Your package Greenhorn and mine Mystery_Monger.Derived have identical specifications, hence your assertion they were the same, alas - Greenhorn is not a child of Mystery_Monger, as you can easily see from its name. Thus Greenhorn.Something (contrary to Mystery_Monger.Derived.Something) does not inherit anything and cannot override the primitive operations Initialize, Adjust, Finalize, see RM_95 7.3.1(6). These declarations therefore do not replace anything, they only introduce new procedures that just happen to be called Initialize, Adjust, Finalize.
During elaboration of your program Surprise, the object Greenhorn.Nothing is created. Since the extension aggregate of Nothing contains a Root component, the latter's Initialize operation is called. The RM allows and AI95_00083 enforces creation "in place", so there's no need to copy and adjust the aggregate. Therefore all you should get is a single call on Root's Initialize, passed the view conversion Root(Nothing).
When Surprise.Thing is elaborated, again Initialize is called for the Root part.
In the assignment Thing:=Nothing; the Root part of Thing is controlled and hence must be finalized; then Nothing is copied to Thing, finally the Root part of Thing must be adjusted.
At scope exit, the Root part of Thing needs to be finalized as must be the Root part of Nothing at program end.
Now you, my dear friend, presumably see where the differences between our seemingly equal programs are hidden. They would in fact be equal if only I had visibly derived the declaration of Root from Ada.Finalization.Controlled. It is a remarkable fact that overriding the primitive operations Initialize etc. in the private part and therefore invisibly is of no relevance at all. To understand the reason, you only have to look at the specification of Ada.Finalization in the language reference manual, chapter 7.6 - the primitive operations RM7.6(6) Initialize, Adjust, Finalize are visible and stay so forever, since hiding of visibly inherited operations is impossible.
Perhaps you now wonder whether overriding inherited operations visibly or invisibly makes any difference at all (mind you, I do not speak of adding).
The surprising answer: Very little.
Actually only a somewhat odd effect comes to my mind concerning the renaming of parameter names. To stay with our example: The original name in RM7.6(6) is Object, I renamed it invisibly to the ouside world in Obj. With named parameter associations, we henceforth have to write Initialize(Obj=>X) respectively Initialize(Object=>X) depending on the local visibility of the declaration. [I fear some compilers will have problems with this.]
In the hope not to have fostered further confusion with my explanations, I remain with my very best wishes for the new millenium (also here widespread confusion is prevalent - actually we are not yet so far)
Yours sincerely Lady Ada