Very confusing example in Function Calls (LearnAda)

I don’t understand at all this program.. It seems to intend to make a point about something, I guess… but then, there is a logic error I think in the Quadruple function..
I’m new to Ada and I can’t go past this example..

Image

The error is correct.
In Ada you cannot simply discard returned values, which is why the language distinguishes between subprograms that return values (function) and those which do not (procedure).

and how would you fix this program to compile? I can’t get it to compile

Delete the line #14. The declaration of Res, line #10, has already called Double twice, we don’t need to call it a third time.

In Ada, you cannot call a function as if it were a statement. You have to use it as an expression, that is, the returned value cannot be ignored, but stored in a variable or used in a wider expression. The point is to teach that this is not possible in Ada, for good reasons, to the contrary of other less safe languages, where you can call functions as if they were procedures (or void functions, as they might call them).

In this case, it doesn’t make sense at all to ignore the returned value. If it did, you have to provide two subprograms, a function returning the value (an ignorable status condition, maybe) and a procedure, which might call the function and leave without use the variable where you store the returned value. The idea behind Ada is that this has to be a conscious decision, to avoid errors like calling the Double function, which does nothing if you ignore the result, besides taking useless CPU cycles.

3 Likes

Yes, but the code still doesn’t compile.. you can try here Subprograms — learn.adacore.com

Could you recommend any books that describe Ada’s memory model, and the rationale behind many of the design choices of the language? thank you!

You need to wrap everything into a procedure (tha main program) also called Quadruple, in order to match the file name…
The setup of this example is unfortunate…

procedure Quadruple is
    
function Quadruple (I : Integer)
                    return Integer is

    function Double (I : Integer)
                     return Integer is
    begin
       return I * 2;
    end Double;

   Res : Integer := Double (Double (I));
   --               ^ Calling the Double
   --                 function
begin
--   Double (I);
   --  ERROR: cannot use call to function
   --         "Double" as a statement

   return Res;
end Quadruple;

   x : Integer;
begin
   x := Quadruple (5);
end;
1 Like

I guess it depends on what you need. The Ada language spec goes to great pains not to define to much of the memory model (from an implementation standpoint), allowing a compiler vendor to implement how they like (at least when possible). Also there isn’t a single memory model, various aspects of the language are different. Is there some areas specifically you are interested in that we could point you to to start with?

For rationale, there is a lot of data. Every decision for the language including discussion is documented as “Ada Issues” or AI’s. All of these are kept here: http://www.ada-auth.org/ais.html

There’s a searchable database with online html / txt versions. They are also available zipped up if you want local copies so you can use local search tools of your choosing.

Additionally, the reference manual comes in two different versions, the standard version and the “annotated” version which has some further discussion on a lot of things. You can find it as a link here (there are links to older versions there as well): http://www.ada-auth.org/standards/ada22.html

Additionally for versions 1983, 1995, 2005 and 2012 there are “Rationale” documents which might be too strongly named as they don’t go deep into actual rationale, but do provide more layman discussion and examples and some use cases for new features for the specific version. For Ada 2022 the author of those decided not to do one, but another ARG member made an “Overview” document that does similar, though on a slightly smaller scale. The nice thing though is the overview does link to the “Ada Issues” (AI’s) for some of the new features so you can check those out and discussions behing.

For more recent stuff, the ARG has started moving to github for a lot of discussions for more recent stuff and possibly future stuff. You can find those discussions in Pull Requests and Issues here: GitHub - Ada-Rapporteur-Group/User-Community-Input: Ada User Community Input Working Group - Github Mirror Prototype

Sorry I don’t have more info for yal. Hopefully that can get you some stuff to start with.

4 Likes

Ada memory model is the model of the underlying OS unless Ada’s stand-alone run-time is used. E.g. if you have a segmented memory machine so be it.

If you rather meant memory management. Ada’s concept is to hide it completely where possible. For example if an argument is passed in the in-out mode it, depending on the type, will be passed by reference or else by value copy-in copy-out transparently to the user.

In short if you do not run out of stack (very large arrays), use some complicated structures (graphs), need aggressive optimization (arena pools) you can safely ignore the memory issue.

4 Likes

Ok, so if the language distinguishes between procedure and function, and you cannot ignore a returned value, then you have to have some place to store that returned value — there are several ways to do this, but one that perhaps doesn’t get enough love is using renames on the result:

function Quadruple (I : Integer)
                    return Integer is

    function Double (I : Integer)
                     return Integer is
    begin
       return I * 2;
    end Double;

   Res : Integer := Double (Double (I));
   --               ^ Calling the Double
   --                 function
begin
   Declare
      Value : Integer renames Double (I);
   Begin
      Null; -- Here is where you would use the results.
   End;

   return Res;
end Quadruple;
1 Like

As far as the rationale goes, there are design documents accompanying Ada 83, Ada 95, Ada 2005, and Ada 2012 editions.

The original Ada 83 rationale is a fundamental Ada design document:
Ada 83 Rationale
Ada 83 Rationale - zip archive

2 Likes

If you’re interested in Ada’s memory management, I think that “Memory Management with Ada 2012” is excellent…if you want even more, look into implementing your own allocator (see System.Storage_Pools, you derive from Root_Storage_Pool), I would recommend (a) implementing a DEBUG_PRINT method to show the contents of the storage-pool, and (b) using an array-field to hold your objects.

2 Likes

Again, thank you everyone! Really appreciate your help and all the valuable insights/links! :grinning_face:

There are “Rationale” documents for Ada 83 and Ada 95 as well. See: Ada Standards - Ada Resource Association

1 Like

Thank you, I have already started reading the Ada Rationale 83, and it’s very interesting

Thanks, I’ll update my post.

1 Like

Why declaring Value which is not used.

The following compiles and work:

with Ada.Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO; 
procedure Main is
   function Quadruple (I : Integer)
                    return Integer is

      function Double (I : Integer)
                     return Integer is
      begin
         return I * 2;
      end Double;

      Res : Integer := Double (Double (I));
      --               ^ Calling the Double
      --                 function
   begin
      return Res;
   end Quadruple;
begin
   Put (Quadruple(42));
end Main;

Because the answer to the question is “you cannot ignore a returned value” — if that is the case, then you must explicitly not use the returned value.

Additionally, if you call the unused variable Dummy instead of Value, GNAT won’t issue a warning when the -gnatwu switch is enabled.