Version 1.1 of acs/ac-00223.txt

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

!standard 6.1(19)          12-01-21 AC95-00223/00
!class Amendment 12-01-21
!status received no action 11-01-21
!status received 11-09-19
!subject New attribute 'Default
!summary
!appendix

From: Martin Dowie
Sent: Monday, September 19, 2011  6:03 AM

I've recently come across a problem which required numerous subprograms to do
something different if the default value of a parameter was passed.

The best solution I could find was to declare a set of constants that were then
used in both the subprogram declaration and within the body of the subprogram
but this was rather annoying due to the large number of constants having to be
declared.

It would have been much cleaner had Ada had an attribute that returned the
default value of any parameter, e.g.

procedure Demo (P : Integer := 10) is
begin
   if P = P'Default then       --  P'Default would return 10 in this case
      -- Do something when default selected
   else
      -- Do something different
   end if;
end Demo;

'Default_Expression might be a better name.

The compiler would easily be able to identify when 'Default(_Expression) was
used when there wasn't an actual value specified.

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

From: Adam Beneschan
Sent: Monday, September 19, 2011  2:45 PM

And if a parameter default expression contains a function call that doesn't
necessarily return the same result every time it's called?

  package Pack1 is

    function Counter return Integer;
    procedure Demo (P : Integer := Counter);

  end Pack1;

  package body Pack1 is

    Current_Counter : Integer := 1;

    function Counter return Integer is
      Result : Integer;
    begin
      Result := Current_Counter;
      Current_Counter := Current_Counter + 1;
      return Result;
    end Counter;

    procedure Demo (P : Integer := Counter) is
    begin
      if P = P'Default then
        ...
      end if;
    end Demo;

  end Pack1;

What are your proposed semantics for this attribute, and how would they work in
this case?

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

From: Martin Dowie
Sent: Monday, September 19, 2011  3:26 PM

Don't support it - static defaults only.

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

From: Pascal
Sent: Monday, September 19, 2011  3:01 PM

Hello.
Maybe with attribute Default or whatever returning Boolean True when no actual
arg is present in the call for the concerned parameter:

   procedure Demo (P : Integer := Counter) is
   begin
     if P'Default then -- true if call to Demo without actual arg for P
       ...
     end if;
   end Demo;

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

From: Randy Brukardt
Sent: Monday, September 19, 2011  3:40 PM

That would require passing a bit-map with every call as to whether or not the
parameter is defaulted. Since the compiler cannot in general know what is in the
body of a subprogram (because of separate compilation), that would apply to
*all* calls of subprograms that have parameters with default expressions. I
think that would be way too much run-time overhead for a feature of marginal
value, especially as overloading can be used to get the same effect without the
run-time overhead.

The original proposal doesn't have this problem (although Adam pointed out a
problem with it as well).

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

From: Riccardo Bernardini
Sent: Monday, September 19, 2011  4:00 PM

I agree that the feature can be in most of cases replaced with overloading, but
the overhead it does not seem excessive.  Maybe I am missing something, but
would not it suffice to just "push" a constant value on the stack?  A 32-bit
integer has enough room for 32 parameters with default, which suffices (I think)
for most cases of interest, so the resulting overhead would be a push and 4
bytes.  (Of course, I am thinking about x86-like architectures, maybe things can
be different in other cases).  If you are in a case where this overhead is not
acceptable, one could define another pragma like No_Default_Attribute to turn
off this feature (selectively or globally).

> The original proposal doesn't have this problem (although Adam pointed out a
> problem with it as well).

The original it has also the advantage that you can use the actual default value
in expressions P'Default * 2 (although I cannot imagine where this could be
useful).

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

From: Randy Brukardt
Sent: Monday, September 19, 2011  4:26 PM

...
> I agree that the feature can be in most of cases replaced with overloading,
> but the overhead it does not seem excessive.  Maybe I am missing something,
> but would not it suffice to just "push" a constant value on the stack?
> A 32-bit integer has enough room for 32 parameters with default, which suffices
> (I think) for most cases of interest, so the resulting overhead would
> be a push and 4 bytes.  (Of course, I am thinking about x86-like architectures,
> maybe things can be different in other cases).  If you are in a case
> where this overhead is not acceptable, one could define another pragma
> like No_Default_Attribute to turn off this feature (selectively or globally).

It's not acceptable because it is on *all* calls, not just the rare few that use
this attribute. And a large part of the execution time of an Ada program is
taken up by making calls.

Specifically, for Janus/Ada on an x86, a call of a two parameter subprogram
requires pushing 4 32 words onto the stack (2 parameters, the return address,
and the display value. Adding a fifth word would make such a call 20% slower! I
cannot imagine how this feature could possibly be worth making calls 20% slower.

Also note that this would make such calls slower compared to other languages
(like C). And it would also add overhead to exported subprograms (for which the
information could not be accurate anyway).

All-in-all, I would guess that this feature alone would make Ada programs 5-10%
slower (by default, at least). It would have to be a *lot* more valuable than
this one is to have such an impact.

> The original it has also the advantage that you can use the actual
> default value in expressions P'Default * 2 (although I cannot imagine
> where this could be useful).

I thought that was the point of the original proposal:

   if Some_Param = Some_Param'Default then ...

Surely equality is an expression!

This second proposal doesn't actually tell you the default value, and as Steve
Baird pointed out privately, that can be different for different calls (a rename
can change defaults). OTOH, the original proposal isn't well-defined because of
the renaming issue, so I don't see it being viable, either.

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

From: Adam Beneschan
Sent: Monday, September 19, 2011  7:14 PM

...
> Maybe with attribute Default or whatever returning Boolean True when
> no = actual arg is present in the call for the concerned parameter:
>
>    procedure Demo (P : Integer := Counter) is
>    begin
>      if P'Default then -- true if call to Demo without actual arg for P
>        ...
>      end if;
>    end Demo;

The biggest problem there is that since, at the time a call to Demo is seen, the
body of Demo may not have been compiled, the compiler can't know whether
'Default is used on any or all of the parameters.  This means that every
subprogram that has any default parameter would have to take an additional
hidden Boolean parameter to indicate whether the parameter is actually present
in the call; thus, every subprogram call becomes a little bit slower even if
'Default isn't used anywhere in the program.  I believe this is "distributed
overhead" that the language designers try to avoid.

If there's just one parameter with a default, the simple solution seems to be to
overload it:

   procedure Demo;
   procedure Demo (P : Integer);

where the bodies of both routines are wrappers for some other routine that takes
a Boolean parameter indicating whether P is present.  I do stuff like this all
the time.  However, I can understand that this could get unwieldy if there are,
say, three parameters with default expressions.

If we want a 'Default boolean attribute, perhaps we need a way to specify that
information about whether a parameter is defaulted should be passed:

   procedure Demo (P : Integer := <default>)
     with Default_Indication(P) => True;

Or some syntax like that.  Then P'Default would be allowed only if there's an
aspect clause like this enabling the use of 'Default.  That would avoid
distributed overhead.

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

From: Steve Baird
Sent: Monday, September 19, 2011  7:49 PM

>  Maybe with attribute Default or whatever returning Boolean True when no
>  actual arg is present in the call for the concerned parameter:
>
>    procedure Demo (P : Integer := Counter) is
>    begin
>      if P'Default then -- true if call to Demo without actual arg for P
>        ...
>      end if;
>    end Demo;

1) Which default are we talking about?

    There are lots of ways to call a given subprogram which can each
    introduce a new set of parameter defaults:
      - by calling a rename of the given subprogram

      - by calling a generic formal subprogram where the
        given subprogram is the corresponding actual subprogram

      - by calling an access to subp value which designates
        the given subprogram

      - by making a dispatching call to a subprogram which
        ends up executing the body of the given subprogram

    For example, in a case like

        procedure P (X : Integer := 111);
        procedure Q (Y : Integer := 222) renames P;
        procedure P (X : Integer := 111) is
        begin
            if X'Defaulted then
                -- is it ok to assume that X = 111 here?
                ...
            end if;
        end P;
     begin
        Q;

    , what is the value of X'Defaulted in the if-statement?

    If the answer is anything other than "ignore all defaults except
    those given with the original subp declaration", then this would
    impose distributed overhead. Even subprograms which don't declare
    defaulted parameter values would have to cope with the case where
    they are passed defaulted actual parameters.

    So let's assume the answer is "ignore all defaults except those
    given with the original subp declaration".

    Even this leads to implementation complexity involving wrappers,
    assuming that support for this attribute involves some change in
    the calling conventions for the subprogram in question (e.g., the
    addition of a parameter as outlined in Riccardo Bernardini's
    message).

    Taking the access attribute of a subprogram which has a defaulted
    parameter would require generating a wrapper routine. The situation
    with primitive operations of tagged types would be peculiar -
    an implementation would need to put a wrapper in the dispatch
    table while keeping the unwrapped version around for use in
    nondispatching calls. So much for the equivalence of
    dispatching and nondispatching calls.

    Which leads to my second point =

2) Replacing a defaulted parameter value with a semantically
    equivalent explicit value should be a semantics-preserving
    transformation.

    This could come up, for example, in a case like transforming
        if Flag then
           P1 (17 parameters, letting the 18th default to 0);
        else
           P2 (the same 17 parameters, Last_Parameteter => 0);
        end if;
    into
       My_Access_to_Subp_Type'(if Flag then P1'Access else P2'Access).all
        (the same 17 parameters, Last_Parameteter => 0);

    Or perhaps in introducing a generic with a formal subprogram in
    order to factor out a bunch of almost-common code which differs
    in that one instance calls Foo where another instance calls Bar.

    We don't want people shying away from such transformations
    because they can't see the body of some subprogram and they
    are worried about breaking something. We want to make
    semantics-preserving transformations easier, not harder.
    We don't want to increase the number of potential tripwires that
    someone maintaining a large body of unfamiliar code has to
    worry about tripping over.

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

From: Adam Beneschan
Sent: Monday, September 19, 2011  8:45 PM

> 2) Replacing a defaulted parameter value with a semantically
>     equivalent explicit value should be a semantics-preserving
>     transformation.

Yes, this makes sense to me.  If you want subprograms that behave differently
depending on whether a parameter is present or not, then what you really want is
either (a) two different subprograms, which could be overloaded subprograms with
the same name, or not; or (b) some sort of "optional parameter" feature that
isn't part of Ada, although I suppose that if we were influenced by a few too
many beers we might be able to design a new syntax like

  procedure Demo (P : Integer := <>);

along with the syntax for the body to determine whether the optional parameter
really was missing.  Whatever design for this feature that we came up with, I'm
sure we'd regret it in the morning.

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

From: Randy Brukardt
Sent: Monday, September 19, 2011 10:26 PM

I think it would take quite a few beers (hopefully something like Capitol Blonde
Dopplebock or Chimay Trappist Ale or maybe even Rochfort 10 :-).

But in any case, it is pretty clear this is a bad idea, and anyway this is not
what Martin asked for.

The more interesting question is whether what Martin asked for is feasible.
Two problems have been noted:
(1) Which default? Renames and the like can change the expression.
(2) Default expressions that have different answers when re-evaluated.

Answering (1) as "the defaults from the original subprogram definition", and (2)
as "re-evaluate each time" (and don't do that if it hurts :-) would probably be
the easiest and most logical answers. [For (2), default expressions are always
re-evaluated when they appear in parameter lists, it would be odd to do anything
else for this attribute.] So the remaining question is whether the feature would
be sufficiently useful for the effort to implement it.

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

From: Georg Bauhaus
Sent: Tuesday, September 20, 2011  5:00 AM

> The more interesting question is whether what Martin asked for is feasible.

From a stylistic point of view, I understand that Ada chooses such that whenever
you have a name in your hand (visibly) to be passed somewhere, then always use
that, even later, not something available indirectly, like 'Default would be, in
a sense.

This is the case with generics such as Ada.Containers.* that don't re-export the
formal Element_Type.  You wouldn't need it where the actual for Element_Type is
visible. (It would also be "more anonymous" than the actual actual.)

Isn't the static value visible in this sense in Martin Dowie's original profile?
Explicit constants global to procedure Demo would seem less burdensome if their
names convey meaning (a meaning not typically inherent in numeric literals).

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

From: Bob Duff
Sent: Tuesday, September 20, 2011  9:45 AM

> Answering (1) as "the defaults from the original subprogram
> definition", and
> (2) as "re-evaluate each time" (and don't do that if it hurts :-)
> would probably be the easiest and most logical answers.

Reasonable.

Or for (2), say 'Default is legal only if the default is a static expression.
Normally, I don't like such rules, but in this case, I don't see any use for
'Default unless it IS static.

>...So the remaining
> question is whether the feature would be sufficiently useful for the
>effort  to implement it.

My opinion: certainly not.

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

From: Steve Baird
Sent: Tuesday, September 20, 2011  10:52 AM

>> ...So the remaining
>> question is whether the feature would be sufficiently useful for the
>> effort to implement it.
>
> My opinion: certainly not.

I agree. The "workaround" of declaring a constant (perhaps one whose name
differs from the desired attribute only in the substitution of an underscore for
an apostrophe) seems fine to me.

For example

     X_Default : constant T := <>;
     procedure Foo (X : T := X_Default) is
     begin
         if X = X_Default then ...
     end Foo;

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

From: Martin Dowie
Sent: Tuesday, September 20, 2011 10:59 AM

> I agree. The "workaround" of declaring a constant (perhaps one whose
> name differs from the desired attribute only in the substitution of an
> underscore for an apostrophe) seems fine to me.

It's ok until you have 200 operations, each with maybe 5 defaulted values... :-(

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

From: Randy Brukardt
Sent: Tuesday, September 20, 2011  1:20 PM

I have to admit, I'm dubious about this "problem".

(I realize that criticizing other people's designs without much information is
treading on rather thin ice, but that's never stopped me before, so here goes...
:-)

It seems to me to be an abuse of the idea of default parameters to care
specially about the default value. The whole idea is that this is a normal value
that doesn't require special treatment. It's probably better to use overloading
(and no default) if special treatment is needed.

I can't recall a single instance in Claw (which has hundreds, perhaps thousands
of defaults) where we had to test for the default value. (There are some cases
where we had to test the parameter value - especially for Boolean parameters -
but that has nothing to do with the default.) There are a number of cases where
we used overloading when the absence of a parameter requires different behavior.

I can imagine that this would happen occasionally, but it doesn't seem to make
sense to happen very often, and as the workaround is pretty easy, there doesn't
seem to be a real need for a special feature. (Yes, the workaround requires more
typing for the extra declaration. Nothing new about that; Ada always requires
more typing than other languages [in order to get more explicitness]. If that's
a real problem, you're using the wrong language [or wrong IDE/Editor] - go use
Forth or Lisp. :-)

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

From: Riccardo Bernardni
Sent: Tuesday, September 20, 2011  1:31 PM

Why not APL? (sorry, couldn't resist :-)

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

From: Gautier de Montmollin
Sent: Tuesday, September 20, 2011  2:29 PM

> It's ok until you have 200 operations, each with maybe 5 defaulted
> values... :-(

Why not having a 6th parameter in front of the 5 other ones:
use_defaults: Boolean:=  True;
?
And/or wrapping some parameters into records, in case you have several default
values at the same time ? It seems like there is something that can be worked
out in the code, before adding something more to the language... Do you have an
example you could post to illustrate the problem ?

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

From: Martin Dowie
Sent: Wednesday, September 21, 2011  3:06 AM

Would need a Boolean per parameter (as passing control parameter) - not a flier.
Also means the user has to know and check if the value they have is the default
(what I'd the default changed?).

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

From: Martin Dowie
Sent: Wednesday, September 21, 2011  3:20 AM

> I have to admit, I'm dubious about this "problem".
>
> (I realize that criticizing other people's designs without much
> information is treading on rather thin ice, but that's never stopped
> me before, so here goes... :-)

:-)

The 'requirement' is to write a thick Ada binding to a 3rd party's command line
interface (the one with ~200 commands!) - so using another language would be a
no-no!

I have competing requirements, as we'd like this new suite of packages to be
user friendly going forward to develop more Ada apps on top (hence defaults for
the 99% of the calls that will ever be made but still having the same options as
the low level commands available).

Competing against that is the requirement to produce the same commands out the
back-end as our current (and working) app does - never underestimate how
important 'working' is! And the working app doesn't include values that are
'default' in the 3rd parties command line.

I agree it's a fairly unusual case (20+ years of Ada and I can't remember
thinking I've needed such an attribute before). But it seemed like a trivial
addition and if you don't ask, you don't get! (or "shy bairns don't get
sweeties" as we'd say in these parts)

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

From: Randy Brukardt
Sent: Wednesday, September 21, 2011  12:38 PM

> The 'requirement' is to write a thick Ada binding to a 3rd party's
> command line interface (the one with ~200 commands!)
> - so using another language would be a no-no!
>
> I have competing requirements, as we'd like this new suite of packages
> to be user friendly going forward to develop more Ada apps on top
> (hence defaults for the 99% of the calls that will ever be made but
> still having the same options as the low level commands available).
>
> Competing against that is the requirement to produce the same commands
> out the back-end as our current (and working) app does - never
> underestimate how important 'working' is! And the working app doesn't
> include values that are 'default' in the 3rd parties command line.

Thanks for the explanation. It sounds like you have a requirement to abuse
default parameters. :-)

I can remember some(s) case where I specially handled the output of some routine
to avoid giving parameters if the value matched their 3rd-party default. I think
that was in order to keep the commands as short as possible, there was some
bound on the length and I wanted to avoid reaching that if possible. (Possibly
that was in the CVS interface, one version of which runs on Ada-auth.org.)
That's fairly similar to your requirement (mine wasn't a big R requirement, but
it was valuable, and might have become a big R requirement had I NOT done it
right away).

But in that case, the defaults that you are interested in are those used by the
3rd-party program, not the ones used in the Ada source. Those *could* be the
same, but there is no reason for that to be the case (and I recall that it
wasn't the case all of the time in my interface, I used the defaults that were
most useful to Ada code; your situation appears to be similar). So it seems to
me that you would be better off putting the default values directly into the
code with suitable comments (I didn't even use constants, because I was treating
the 3rd-party interface as unchanging -- maybe a bad choice), rather than
assuming some correspondence between the defaults in the Ada context and in the
3rd-party context.

Of course if you do that, you couldn't use the attribute even if it existed.

> I agree it's a fairly unusual case (20+ years of Ada and I can't
> remember thinking I've needed such an attribute before). But it seemed
> like a trivial addition and if you don't ask, you don't get! (or "shy
> bairns don't get sweeties"
> as we'd say in these parts)

Which is the other problem. The need seems to be pretty rare, and since the
definition is nowhere near as trivial as it originally appears (Steve has been
privately sending me additional issues involving the defaults for inherited
routines - it's scary how his mind works...:-), it seems like more trouble than
it would be worth.

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

From: Martin Dowie
Sent: Thursday, September 22, 2011  6:19 AM

>Thanks for the explanation. It sounds like you have a requirement to
>abuse default parameters. :-)
>
>[snip]
>
>Of course if you do that, you couldn't use the attribute even if it
>existed.

Which is where I am now and I need to finish this sooner rather than later, so
I'm just living with the extra constants.

>Which is the other problem. The need seems to be pretty rare, and since
>the definition is nowhere near as trivial as it originally appears
>(Steve has been privately sending me additional issues involving the
>defaults for inherited routines - it's scary how his mind works...:-),
>it seems like more trouble than it would be worth.

Fairly enough

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

From: Gautier de Montmollin
Sent: Thursday, September 22, 2011  7:15 AM

> The 'requirement' is to write a thick Ada binding to a 3rd party's command
> line interface (the one with ~200 commands!) - so using another language would
> be a no-no!

OK.
In the CLI, if you are passing explicitly a value which coincides with the
default value, does it have the same effect as letting the parameter blank and
having the default value used ? (just wondering why you need to test the value
being the default one and make a special case).

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

Questions? Ask the ACAA Technical Agent