Version 1.1 of acs/ac-00329.txt

Unformatted version of acs/ac-00329.txt version 1.1
Other versions for file acs/ac-00329.txt

!standard 4.5.9(2/5)          20-06-05 AC95-00329/00
!class Amendment 20-06-05
!status received no action 20-06-05
!status received 20-05-26
!subject Declare expressions with exception handlers
!summary
!appendix

From: Pascal Pignard
Sent: Tuesday, May 26, 2020   2:58 AM

Several times, I faced exceptions occurring in the declaring part of a
sub-program without being able to catch them locally to the sub-program as the
exception handler of the sub-program catch only exceptions of its body.

For instance an exception in Decode is not caught locally in P1:

procedure P1 (UTF_8_Str : String) is
  Str : String := Decode (UTF_8_Str);
  Upper_Str : String := To_Upper (Str);
begin
  -- process Upper_Str
  ...
exception
  -- don't catch exception in declarative part!
end;

An extension of AI12-0236-1 "declare expressions" might add an exception part
with a default expression in case of exceptions, perhaps like that:

procedure P2 (UTF_8_Str : String) is
  Str : String := (declare
                         begin
                             Decode (UTF_8_Str)
                         exception
                             when Encoding_Error =>
                                  "==error==");
  Upper_Str : String := To_Upper (Str);
begin
  -- process Upper_Str
  ...
end;

An alternative is of course to add a declare statement:
procedure P3 (UTF_8_Str : String) is
begin
  declare
    Str : String := Decode (UTF_8_Str);
    Upper_Str : String := To_Upper (Str);
  begin
    -- process Upper_Str
    ...
  end;
exception
-- catch exception in declare part
      when Encoding_Error =>
           P3 ("==error==");
end;

But the result is not really satisfying as P3 is called again (or code has to be
duplicate) in exception handler.

What are your feedbacks?

***************************************************************

From: Jean-Pierre Rosen
Sent: Tuesday, May 26, 2020   5:21 AM

> procedure P1 (UTF_8_Str : String) is
>   Str : String := Decode (UTF_8_Str);
>   Upper_Str : String := To_Upper (Str); begin
>   -- process Upper_Str
>   ...
> exception
>   -- don't catch exception in declarative part!
> end;

This behaviour is absolutely necessary: assume Str raises an exception, if it
were caught by the handler, the handler could access Upper_Str, which has not
been elaborated. You cannot access an object that has not been elaborated!

> An extension of AI12-0236-1 "declare expressions" might add an exception part
> with a default expression in case of exceptions, perhaps like that:
>
> procedure P2 (UTF_8_Str : String) is
>   Str : String := (declare
>                          begin
>                              Decode (UTF_8_Str)
>                          exception
>                              when Encoding_Error =>
>                                   "==error==");
>   Upper_Str : String := To_Upper (Str); begin
>   -- process Upper_Str
>   ...
> end;

This is certainly /possible/, the real question is: is it worth the complexity?

...
> But the result is not really satisfying as P3 is called again (or code has to
> be duplicate) in exception handler.

My solution would use a local function (no need for a local block):

procedure P4 (UTF_8_Str : String) is
   function Local_Decode (S : String) is
   begin
      return Decode (S);
   exception
      when Encoding_Error =>
         return "==error==";
   end;

    Str : String := Local_Decode (UTF_8_Str);
    Upper_Str : String := To_Upper (Str);
begin
    -- process Upper_Str
    ...
end;

***************************************************************

From: Tucker Taft
Sent: Tuesday, May 26, 2020   7:10 AM

I would agree with Jean-Pierre's suggestion of creating a local function that
includes a handler.  Also in that way you can use it multiple times if you are
initializing several strings with, for example, Decode.

Declare expressions are really about *expressions" while exception handlers
generally involve a sequence of statements.  Also, declare expressions are
mainly designed for places, like pre- or postconditions, where you  have
potentially complex expressions and cannot write statements.  When you are in
the declarative part of a subprogram, you can declare as many nested subprograms
as you need to factor out, for example, some of the exception handling.

***************************************************************

From: Richard Wai
Sent: Tuesday, May 26, 2020   9:56 AM

I agree with Tucker and Jean-Pierre as well. Adding exceptions to declare
expressions probably helps the bad habit of cramming as much as possible into an
expression. It becomes very messy very quickly. As Ada programmers, I think we
should always prioritize readability over "conciseness" (concise in the APL
sense) where it's not possible to have both. The example you gave seems fine,
but as one adds more exception choices, and possibly adding complex expressions
within those choices, things can get out of hand quite quickly. This is already
a problem with what we have currently, and so I echo Tucker's emphasis that most
of these expression constructs are intended to facilitate pre/post conditions. I
think the goal is to provide the bare minimum to enable the expression of
contracts, without going to far to encourage a quasi-functional programming
paradigm inside of Ada.

I think we've all experienced the trouble you face at one time or another, and I
think structuring the problem out more into nested subprograms (a very Ada thing
to do!) is the best way to keep things readable.

***************************************************************

From: Jeff Cousins
Sent: Tuesday, May 26, 2020   9:19 AM

> Several times, I faced exceptions occurring in the declaring part of a
> sub-program without being able to catch them locally to the sub-program as
> the exception handler of the sub-program catch only exceptions of its body.

I find this behaviour a real pain too.  A drastic change such as exception
handlers should catch exceptions in the declarative part too (what I'd really
like) would be too backwardly incompatible. Your suggestion (exception parts to
declare expressions) and Jean-Pierre's suggestion (local function) both require
thinking in C++ mode and having to consciously think "might this bit of code
raise an exception?" and if so explicitly add something around that bit of code
(in C++, a try/catch block).

***************************************************************

From: Jean-Pierre Rosen
Sent: Tuesday, May 26, 2020  11:13 AM

But please explain how you could prevent the exception handler from accessing
objects in the declarative part that have not been elaborated.

***************************************************************

From: Jeff Cousins
Sent: Tuesday, May 26, 2020  11:54 AM

It would have to be restricted, such as just enough to output what subprogram it
is and the parameters passed in. Probably tricky and possibly impossible if not
designed in from the start.

***************************************************************

From: Christoph Grein
Sent: Wednesday, May 27, 2020   1:47 AM

> ... require thinking in C++ mode and having to consciously think "might this
> bit of code raise an exception?" and if so explicitly add something around
> that bit of code (in C++, a try/catch block).

Ahem, I think the whole task of programming consists of conciously thinking
about what the code will do. Exception handlers are no exception (coincidential
pun :-) to this.

***************************************************************

From: Jeffrey R. Carter
Sent: Tuesday, May 26, 2020   2:01 AM

>>>   Several times, I faced exceptions occurring in the declaring part of
>>>   a sub-program without being able to catch them locally to the
>>>   sub-program as the exception handler of the sub-program catch only
>>>   exceptions of its body.
>>
>> I find this behaviour a real pain too. A drastic change such as
>> exception handlers should catch exceptions in the declarative part
>> too
> But please explain how you could prevent the exception handler from
> accessing objects in the declarative part that have not been elaborated.

There are languages in which declarations and statements can be freely
intermixed, and which have exception handlers, so there must be some alternative
way of looking at this in which such a handler works. I'm not suggesting
adopting such an approach, but presumably the way Ada deals with this is not the
only way.

***************************************************************

From: Tucker Taft
Sent: Friday, May 29, 2020   7:38 AM

True, but in a typical "try ... catch" none of the variables declared inside the
"try" block are visible in the "catch" block.  And none of the exceptions raised
before the "try" are handled by the "catch".  So I think the situations are
equivalent.

***************************************************************

From: Jeffrey R. Carter
Sent: Friday, May 29, 2020   8:49 AM

> True, but in a typical "try ... catch" none of the variables declared
> inside the "try" block are visible in the "catch" block. And none of
> the exceptions raised before the "try" are handled by the "catch". So
> I think the situations are equivalent.

I don't think they are equivalent, but I also don't think this is an appropriate
place to discuss this.

***************************************************************

From: Tucker Taft
Sent: Friday, May 29, 2020   3:11 PM

I'm surprised you say that.  The "begin" is equivalent to the "try" and the
"exception when ..." is equivalent to the "catch" in this analogy.  In any case,
we can move our discussion to "C++-Comment" ;-)

***************************************************************


Questions? Ask the ACAA Technical Agent