How to abbreviate long package names

I’m learning Ada. I find some of the package names, like Ada.Strings.Unbounded, are too much typing (as in typewriter typing, not what types objects are), but I prefer not to use the use statement because then I can’t remember which procedures and functions came from where. So I came up with a way to abbreviate Ada.Strings.Unbounded as Asu, and Ada.Text_IO as Atio.

My question is whether what I’m doing is the best way to abbreviate long package names. My code follows:

steves_abbrevs.ads follows:

with Ada.Text_IO;
with Ada.Strings.Unbounded;
package Steves_Abbrevs is
	package Atio renames Ada.Text_IO;
	package Asu renames Ada.Strings.Unbounded;
end Steves_Abbrevs;

Main program abbrev_example code follows:

with Steves_Abbrevs;
use Steves_Abbrevs;

procedure Abbrev_Example is
   Out_String: Asu.Unbounded_String;
begin
   Out_String := Asu.To_Unbounded_String("My abbrev example!.");
   Atio.Put_Line(Asu.To_String(Out_String));
end Abbrev_Example;

Is there a better way for me to have done this?

Thanks,

Steve

In short, “whatever rocks your boat”. Literature shows all kinds of practices, some hate the use clause, others don’t care. Renaming package was common. So you’re good to go.

3 Likes

Check out Ada’s renames feature.

With
Ada.Strings.Unbounded_Strings,
Ada.Characters.Handling;
Package Example is
  Package ACH renames Ada.Characters.Handling;
  Package  US renames Ada.Strings.Unbounded_Strings;
  --…
End Example;

I usually use a use clause inside deeper scopes if I want to avoid qualifying with full package names. For someone reviewing the code, the local use of the use clause hints where some things may originate from.


procedure Local_Use_Example is
   Use Ada.Strings.Unbounded;
   Use Ada.Text_IO;
   Out_String: Unbounded_String;
begin
   Out_String := To_Unbounded_String("My example!.");
  Put_Line(To_String(Out_String));
end Abbrev_Example;

1 Like

AdaCore used to do this. Nowadays, they use a lot (far too much, IMO).

1 Like

For me I have different considerations:
If it is a package that is universally useful across the whole of my current unit, is a well known package, and is unlikely to have ambiguous issues (like Ada.Text_IO), then I might just use it for the file. However, I try to keep those to a minimum.

Outside of that I generally either use package renames (though I do them locally instead of a separate file) or I will put the use more locally (like in a specific procedure):

procedure Do_Something is
   use Some_Package;
begin
   -- stuff
end Do_Something;

Also note that if you do a global renaming like that you don’t need an enclosing package if you are ok with multiple files. You can do them individually if you like (this is not my preference):

atio.ads

with Ada.Text_IO;
package Atio renames Ada.Text_IO;

My opinion is that my fingers aren’t tired. I don’t need use or renames in general. I do rename e.g. a device name as it is used many times and the package is e.g. the EXTI package so there is no ambiguity. So I use renames and use only in rare cases.

I know that AdaCores learn site would benefit from dropping use.

My eyes are old and unfortunately they don’t work correctly, so in my case I have to balance readability. I prefer the spelled out sentence like syntax of Ada, but sometimes too much is just not practical for me to see correctly. I find renames of longer items sometimes useful for readability. I like to keep them as local as makes sense though.

The other thing I have found is in some cases, renames really help portability later. Being able to change a single rename clause to change up implementations is really useful in the things I do.

What do you do when you don’t need all of the packages in your abbreviation package? When you want a renaming of a long-named package not in your abbreviation package? Certainly there are times when a shorter name is useful, but this doesn’t seem like a good approach in general.

1 Like

JC001, my plan is to put my most commonly used abbreviations in one .ads file. For the others, I can either use separate .ads files for each seldom used abbreviation, or use a use command within a procedure/function, or just spell the whole thing out. Thanks!

There’s a guide about Being more terse in general on ada-lang.io

After a few times of typing out a multi-level package name it gets a package rename for me. The common ones I use are:

package AIO renames Ada.Text_IO;
package ASU renames Ada.Strings.Unbounded;
package ACH renames Ada.Characters.Handling;

I avoid use Package.Name; since it makes it difficult to know where subprograms, types and enums are brought in from, though you can confine these to a particular subprogram.

Excessive verboseness of your own package names can be an indication of improper package structure. I’ve noticed that it sometimes indicates a sibling package should really be the parent of the current unit.

On the issue of applying the use (apologies for slightly hijacking this thread) being conservative makes a lot of sense but sometimes it can force some things to be a bit awkward. For example, given the following:

package My_Enums is

   type My_Enum is (My_Enum_1, My_Enum_2, My_Enum_3);

end My_Enums;

and a simple client:

with My_Enums;

procedure My_Enums_Client is
   E1 : constant My_Enums.My_Enum :=  My_Enums.My_Enum_2;
   E2 : constant My_Enums.My_Enum :=  My_Enums.My_Enum_1;
   Impossible : exception;
begin
   if E1 = E2 then
      raise Impossible;
   end if;
end My_Enums_Client;

then that fails to compile because the = isn’t visible without use-ing the package. One way to fix that if use is to be avoided alltogether is to roll-out the manual version i.e. :

...........................
if My_Enums."="(E1, E2) then
   raise Impossible;
end if;
...........................

which arguably isn’t massively pretty.

I don’t suppose there’s a workaround for that short of use-ing the package in the declarative part, is there? (like a compiler flag which drops the use requirement for intrinsic-like subprograms?)

By the way, with the above second version of = there are no compiler warnings, however if I use the package (and then simply do if E1 = E2 then) then I’m getting a Condition is always false warning which is great that it picks that up but it’s interesting how it’s not picking up the first version? (project is setup with gnat_native=14.2.1 & gprbuild=22.0.1 in Alire)

Two techniques I’ve used:

  • use all type My_Enums.My_Enum. Normally I avoid use statements but when I’m importing a type I seem to do this.
  • rename:
       procedure "="(E1, E2: My_Enums.My_Enum) returns Boolean renames My_Enums."=";
    
    …and from then on you can just write E1 = E2. I’ve used this sometimes.

Offhand I don’t know the answer to your second question & I have to run. If someone else hasn’t handled it later I’ll take another look.

2 Likes

You don’t have to use the package you can import all operators for only a specific type:

with My_Enums;

procedure My_Enums_Client is
   E1 : constant My_Enums.My_Enum :=  My_Enums.My_Enum_2;
   E2 : constant My_Enums.My_Enum :=  My_Enums.My_Enum_1;
   Impossible : exception;
   use type My_Enums.My_Enum;  -- brings in all operators
begin
   if E1 = E2 then
      raise Impossible;
   end if;
end My_Enums_Client;

Full example online: Online Compiler and Editor/IDE for Java, C, C++, PHP, Python, Ruby, Perl - Code and Run Online

If you want all primitive operations for the type, then you upgrade to

   use all type My_Enums.My_Enum;  -- brings in all primitive operations

The rest of the package is not use visible doing it this way. Also constraining it to the operation in question keeps it localized to just that subprogram and not the rest of your package if it is also in a package.

4 Likes

Use statements are very useful for several conditions:

  1. In the Spec, global, when the package itself is intrinsically tied to the types being imported in even the interfacing.
  2. In the Body, global, when the operations being used are intrinsically tied to the operations being imported; (e.g. Use Ada.Strings.Fixed for a string-manipulation package.)
  3. In the body of a subprogram, locally, or task when many types or operations are used, or when usage is very frequent;
  4. In a Declare-block for cleaning up code aestetically. (In this sense, it’s kinda/sorta like Pascal’s with-block, though on the namespace rather than some named structure.)
2 Likes

Thanks @cantanima and @jere for that, I remember reading about use (all) type but totally forgot it existed!
Applying that in the declarative section of a subprogram sounds like a good compromise between full and localised visibility (especially if a subprogram makes heavy use of a relatively small number of packages).

Ada 95 Problem Solving and program design; Feldman & Koffman

“The Use clause: This is introduced in Chapter 8. Ada industry practice generally avoids the use clause for a number of good reasons. We avoid it here, in general, because qualifying all references to package resources helps the student to really understand which resources are provided by which libraries.”

1 Like