comp.lang.ada
 help / color / mirror / Atom feed
From: Niklas Holsti <niklas.holsti@tidorum.invalid>
Subject: Re: A suggestion for resource management
Date: Mon, 23 Aug 2010 15:25:00 +0300
Date: 2010-08-23T15:25:00+03:00	[thread overview]
Message-ID: <8df7ksFu5fU1@mid.individual.net> (raw)
In-Reply-To: <878w3yhbi2.fsf@mid.deneb.enyo.de>

Florian Weimer wrote:
> * Niklas Holsti:
> 
>>> In a sense, it's just repeating what Unchecked_Deallaction does behind
>>> the scenes.
>> I don't understand your comment at all. An instance of
>> Unchecked_Deallocation is explicitly executed where it is called, not
>> at some later point in time, and no pragma is involved. Or do you mean
>> Finalize for controlled types?
> 
> Exactly, Unchecked_Deallocation arranges for calls to a somewhat
> non-obvious set of Finalize subprogramms.

Yes, but these Finalize calls occur within the execution of an explicit 
call of Unchecked_Deallocation, not "delayed" at some later point in the 
program. If I call Foo (X), it is normal for Foo to do something with/to 
X, including calling primitive operations of X. When 
Unchecked_Deallocation (X) calls Finalize (X), it is thus within (my 
expectation of) normal control flow.

The scheduling of a clean-up action for later execution is more like the 
*implicit* calls of Finalize that occur when a controlled object goes 
out of scope.

>> However, if the evaluate-and-save is *only* equivalent to renaming,
>> you can get weird effects where a scalar variable seems to have
>> different values at different places in the parameter list. For
>> example, consider (using the pragma syntax):
> 
> Oh, so scalars need to be copied after all. 8-)

That does not solve the basic problem, because then, in "pragma Scoped 
(Foo (X))", the value (old or new) of X that is (eventually) used in the 
call would depend on whether X is scalar or not, which would be very 
error-prone under maintenance. Also, what if X is a private type defined 
in another package? Depending on its scalarity would break privacy.

I still think that all evaluation of the clean-up code should be delayed 
until the clean-up is actually needed. If something needs to be saved at 
the point of the pragma Scoped, the programmer should explicitly save it.

This could be assisted by an extension of the "at end" syntax to include 
an optional set of declarations of objects that are elaborated at once, 
but available later (and only) in the clean-up code, for example in this 
way:

    procedure Bar (...)
    ...
       X : X_Type := Old_Value;

       at end of Bar   -- Not (yet) Ada!
          with
             Saved_X : constant X_Type := X;
             -- Takes a copy of X as it is now (Old_Value).
             -- The name Saved_X is visible only here
             -- and in the "do" part.
          do
             Foo (Saved_X);

       -- The name Saved_X is no longer visible.

    begin
       ...
       X := New_Value;
       ...
    end Bar;
    -- Here Foo is called with Saved_X (= Old_Value).
    -- If X_Type needs finalization, Saved_X is finalized
    -- immediately after this call of Foo.

>> Moreover, what happens if an actual parameter is a function call?
>> Should the function be called at the point of the "pragma Scoped", or
>> later?
> 
> At the point of the pragma.

Thus, if the function returns a large composite object, that would have 
to be saved. Another reason to delay all evaluation until the actual use 
of the clean-up code.

Do you have an example that motivates why the parameters should be 
evaluated early, as you suggest?

>> ... you would only allow pragma
>> Scoped on the "top" level in a handled_sequence_of_statements.
>>
>> But why make this limitation? It seems to me not unlikely that some
>> application may conditionally Open a COBOL_File, and then a similarly
>> conditional Close should occur at the end. One could even let "pragma
>> Scoped" be used in loops and execute the clean-up code just before the
>> present iteration of the loop ends.
> 
> There is an ambiguity whether the cleanup will occur at the end of the
> sequence_of_statements, or at the end of some outer scope.  As a case
> in point, in the "if" case, you suggest to prefer outer scope, but for
> the loop case, you want local cleanup.

The choice in the loop case is rather evident, isn't it? The program 
should not be allowed to make two Open calls without an intervening 
Close call, just as you implied in your original posting.

In the "if" case, a choice would have to be made. Choosing the end of 
the enclosing subprogram (or loop) is the more flexible choice, since 
the programmer can force the other alternative by nesting a begin-end 
block in the "if".

With the "at end of <identifier> do" syntax, the clean-up point can be 
indicated by the <identifier>, which could name a subprogram, a loop, or 
a block.

> I couldn't decide which one is
> the right approach, so I made both illegal.

A conservative choice, but I could live with it.

>>> Most languages which strive for
>>> exception safety are gradually moving to different constructs (even
>>> Java).
>> That could be an argument. Can you present these "different
>> constructs" here as examples that we could compare to "finally" and to
>> your proposal?

Many thanks, your links are interesting references. Below, I comment on 
the properties of each, relative to the questions we are discussing, and 
which are:
(1): Do we need a new feature to schedule clean-up actions?
(2): The syntax of the feature.
(3): The form of the clean-up action.
(4): Possible early evaluation and saving of clean-up parameters.

> Python:
> 
>   <http://docs.python.org/reference/compound_stmts.html#with>
>   <http://www.python.org/dev/peps/pep-0343/>

Need (1): Replaces a fairly complex nesting of try-except-finally, so 
may well be useful and needed.

Syntax (2): Uses a specific syntax to define the objects ("context 
managers") that initialize and finalize resources, and to delimit the 
sequence of "subordinate" statements after which the clean-up should 
occur. Thus more "structured" than Florian's proposal for Ada, whether 
by "pragma Scoped" or by "at end do", neither of which delimits the 
subordinate statements (they are all the remaining statements in the 
handled_sequence_of_statements).

Form of clean-up (3): Call of the "exit" method for the context manager 
object(s), with parameters that indicate normal or exceptional 
completion of the subordinate statements. Thus only a single form is 
allowed, and the present proposals for Ada are more flexible.

Saving parameters (4): Only the identity of the "exit" method for the 
context manager is saved. The "exit" method has no programmer-defined 
parameters, so the question of saving parameter values does not arise. 
However, the construct also creates the context manager object, so 
values can be saved there, if the programmer desires. This is similar to 
my suggestion (no automatic early evaluation and saving).

> Java:
> 
>   <http://blogs.sun.com/darcy/entry/project_coin_updated_arm_spec>

Need (1): Replaces a nesting of try-catches-finally, in the same way as 
for Python.

Syntax (2): Extends the Java "try" statement to include a "resource 
specification" part. Similar to the Python syntax, and thus similarly 
different from the proposed Ada syntax. Complicated by the existence of 
"suppressed exceptions" in Java (a new concept for me).

Form of clean-up (3): Call of the parameterless "close" method for the 
resource object(s), thus more restricted even than the Python case.

Saving parameters (4): None for the parameterless "close", but, as in 
the Python case, the resource object is created as part of the construct 
and can thus save state, if the programmer desires.

> C# (which contained it from the start):
> 
>   <http://msdn.microsoft.com/en-us/library/yh598w02%28VS.80%29.aspx>

Very similar to the Java case, but seems a bit simpler; the translation 
is a "try-finally" statement with no "catches" and no nesting. The 
"resource objects" are immutable in the subordinate statements, perhaps 
to ensure that state is saved for the clean-up (point 4).

> Go (with some peculiar semantics):
> 
>   <http://blog.golang.org/2010/08/defer-panic-and-recover.html>
>   <http://golang.org/doc/go_spec.html#Handling_panics>

Need (1): The feature is motivated in the same way as a "finally" 
construct, or automatic finalization of objects, which seem absent from 
Go (caution: this is the first time I look at Go).

Syntax (2): A "defer" statement which is very similar to the proposed 
"pragma Scoped" or "at end do". A clean-up action is dynamically 
scheduled to be executed at the end of the containing subprogram, and 
the syntax does not delimit or contain the subordinate statements. The 
"defer" statement does not identify the relevant resource objects or 
context managers, to use terms from other languages.

Form of clean-up (3): A function or method call. The function can be 
given "in-line" in the defer statement (a "function literal"). 
Flexibility thus similar to the proposed "at end do <statement>".

Saving parameters (4): The parameters to the deferred function or method 
call are evaluated and saved. IIUC this is a real copy, not a renaming, 
and differs from a deep copy only if the parameter value is a pointer or 
has pointer components. No motivation is given for this level of "saving".

Florian's proposal is thus most similar to the Go "defer" statement, and 
rather different from the constructs in Python, Java, and C#.

By the way, Florian, in your original posting you said:

Florian Weimer wrote:
 > Here's a proposal for a resource management pragma. It is similar
 > to the scope(exit) directive in D, the C++ scope guard idom, and
 > (to a lesser degree) Go's rescue statement.

It seems to me that the Go "rescue" statement is Go's form of an 
exception handler/catcher, and not at all like pragma Scoped. I assume 
you meant the "defer" statement, Florian, is that right?

I found the D construct "scope(exit)" explained here:

http://www.digitalmars.com/d/2.0/statement.html#ScopeGuardStatement

My analysis of the D feature:

Need (1): Not explained. There are three variants of the feature, 
respectively for executing a clean-up always, on normal termination, or 
on exceptional termination. If several variants are used in the same 
scope, the corresponding try-catch-finally structure could be quite complex.

Syntax (2): Similar to "defer" in Go and to the proposed "pragma Scoped" 
or "at end do". Does not use a pragma. The subordinate statements are 
not enclosed, and the resource objects are not identified.

Form of clean-up (3): Any kind of statement (but one that must not 
propagate an exception), thus similar to "at end do".

Saving parameters (4): None, as far as I can see.

> Perl 6 apparently has LEAVE (as a more dynamic variant of END), but I
> don't know if it is actually used.

I did not look at this one. Sorry, but my loathing of Perl was too 
strong :-)

My summary of the comparison:

Need (1): Ok, there seems to  be a need, or a least a trend, for this 
kind of thing, so we should at least consider it for Ada too.

Syntax (2):

- Florian's proposal is unique in suggesting a "pragma" syntax. All the 
other languages use syntax that is made part of the core language and is 
not just a "compiler directive".

- The other languages are split between a structured (nested) syntax and 
a more execution-oriented syntax. In the former, the subordinate 
statements are enclosed in the syntactic structure like the (extended) 
Java "try" statement. In the latter, for example the Go "defer" and the 
D "scope(exit)", the subordinate statements are all the remaining 
statements in the enclosing subprogram or scope. Florian's proposal for 
Ada falls in the latter camp.

Form of clean-up (3): Some languages limit this to a single call of a 
subprogram, sometimes a specific subprogram or method (close, exit), 
perhaps even parameterless. Florian's proposal of a call to a 
programmer-chosen subprogram, with possible programmer-chosen 
parameters, is more flexible. But some other languages allow any 
statement as clean-up code.

Saving parameters (4): Only the Go language and Florian's proposal have 
this feature. I think it needs much more motivation to be supported, and 
I continue to think that it will be hard to define in a clear way for 
Ada, especially if arbitrary statements are allowed as clean-up code.

One point that is not addressed in Florian's proposal is making the 
clean-up code aware of the exceptional or normal termination of the 
subordinate statements. This is possible in Python and D. Should it be 
possible in Ada?

> For evidence that try-finally doesn't work, it's instructive to run a
> suitable static analyzer on a code base for the first time.  Even if
> the developers are competent, you'll find rule violations, several of
> them caused by a desire to reduce syntactic overhead.

Concrete examples would be interesting.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .



  reply	other threads:[~2010-08-23 12:25 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-08-21 16:20 A suggestion for resource management Florian Weimer
2010-08-21 19:07 ` Dmitry A. Kazakov
2010-08-21 19:47   ` Florian Weimer
2010-08-21 20:53     ` Dmitry A. Kazakov
2010-08-21 21:09       ` Florian Weimer
2010-08-22  6:40         ` Dmitry A. Kazakov
2010-08-23 23:22         ` Randy Brukardt
2010-08-21 20:34 ` Niklas Holsti
2010-08-21 21:01   ` Florian Weimer
2010-08-22 10:53     ` Niklas Holsti
2010-08-22 15:29       ` Florian Weimer
2010-08-22 16:12       ` Florian Weimer
2010-08-23 12:25         ` Niklas Holsti [this message]
2010-09-04 19:09           ` Florian Weimer
2010-09-07 10:14             ` Niklas Holsti
2010-08-22 11:32     ` Georg Bauhaus
2010-08-23 23:37       ` Randy Brukardt
2010-08-22 13:09 ` stefan-lucks
2010-08-22 14:30   ` Florian Weimer
2010-08-22 15:09   ` Florian Weimer
replies disabled

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox