Object-oriented methods in Ada

Having written Java code for a few decades, there are some facets that I find to be a “must” (without having any good reason for it). One such thing is the way object-orientation makes use of methods. So, while I now try to learn myself Ada (have some PL/SQL experience), I find myself searching for method declaration.

As everyone who has coded Java knows, a method is written as a function within the class declaration. When the method is called, it is called as something that belongs to the class (or rather the object).

Ex. A Java class C may have a method m(x, y, …). And object o of class C can then use this method through the call:
o.m(x, y, …)

Having search (or skimmed) through both literature and the net, it is my understanding that the closet we get in Ada is to have a procedure or a function, with the first argument being of the containing type, declared in the type declaration, and then calling that proc/func by passing a variable of that type as the parameter (sorry if I mess this up, I know what I’m thinking, but putting it in writing is less clear). This would lead to a proc/func call similar to:

m(o, x, y, …)

Or have I missed something?

What is the “correct” object oriented way of writing the following in Ada?
public class Hello
{
public void SayHello()
{
System.out.println( “Hello World!”);
}
}

And similar upon use:
{
Hello greeting = new Hello();
greeting.SayHello();
}

Your help and patience are appreciated.

:slight_smile: Roald

Ada’s tagged types are equivalent to Java classes. tagged is meant to be used when you need inheritance, but is often added just to get the “dot syntax” for calling procedures and functions. Note that the . implies a dereference, so this may have performance implications. In practice, the impact is negligible.

Your example above would look something like this, with the tagged type defined in a package specification or declaration block.

app_namespace.ads

package App_Namespace is

   type Hello is tagged null record;

   procedure Say_Hello
      (This : in out Hello);

end App_Namespace;

app_namespace.adb

with Ada.Text_IO;

package body App_Namespace is
   procedure Say_Hello
      (This : in out Hello)
   is
   begin
      Ada.Text_IO.Put_Line ("Hello, Ada!");
   end Say_Hello;
end App_Namespace;

main.adb

with App_Namespace; use App_Namespace;

procedure Main is
   Greeting : Hello;
begin
   Greeting.Say_Hello;
end Main;

See also: Classes and Object Oriented Programming — learn.adacore.com

There is a proposal to support the dot syntax for all types, regardless of whether they’re tagged or not:

3 Likes

Thank you for a very clear example and explanation. This is not far away from what I concluded. I was, however, confused about the declaration of the methods parameters (with the use of the first parameter).
Also, thank you for the reference link. I did read this before I posted the question, but obviously I misunderstood some of it.

Some of us would prefer not to “pollute the namespace” by avoiding use where possible:

with App_Namespace;

procedure Main is
   Greeting : App_Namespace.Hello;
begin
   Greeting.Say_Hello;
end Main;
3 Likes

I’m still on the fence as to whether I prefer to use “use” or not. I can see the point about polluting the namespace. Back in my Pascal days, I would prefer not to use the “with” statement for similar reason. While coding Java, I am happy that an “import” implied the “with”+“use” construct, since fully package-qualified class paths would become very tiresome, and make the code much less readable.

One option which helps avoid fully-qualified paths is to rename a package:

package Text_IO renames Ada.Text_IO;
Text_IO.Put_Line("Hello, world!"); -- works, and you can use a shorter name, too
1 Like

Another option is to use only the package hierarchy, but not the final package. In that way, name clashes are minimal and comprehension is not hindered. Example:

use Ada;
use Ada.Containers;
use Gtk;

In general practice, I use the fully-qualified names.

1 Like

Packages including nested packages can also provide dot syntax. I prefer fully qualified names too unless from the same package or parent package. Especially in the middle of a refactor. Even on learn.adacore.com I would have found removing use helpful to point out what is what when I was unfamiliar. The problem is that some already see Ada as verbose, I guess. I am not sure if those criticism are born from those who are just trying to defend C without an open mind though.

Just wanted to add some clarification here. In Ada, it doesn’t matter if the containing type is the first argument, the last argument, or somewhere in the middle. Ada compilers can still associate the function or procedure as the equivalent of a class method. Being the first argument does allow the Object.Method usage though, so that is helpful. In addition, functions that return the containing type are also considered the equivalent of a method and can be overridden, so long as they are in the same package as the class type declaration. So for example:

package App_Namespace is

   type Hello is tagged null record;

   procedure Say_Hello
      (This : in out Hello);

   procedure Say_Other_Things(Value : Integer; This : Hello);

   function Construct return Hello;

end App_Namespace;

are all the equivalent of “methods” for Hello.

In Ada, the terminology for “methods” is “primitive operations”.

Also side note, to call a function primitive operation that returns the containing type but doesn’t have an argument of the containing type, you can’t use the Object.Method notation discussed above by Jeremy. You still have to use Package.Method in that case, but any procedure or function that has an argument of the containing type can use Object.Method.

procedure Test is
   Hello : App_Namespace.Hello := App_Namespace.Construct;
begin
   Hello.Say_Hello;
   App_Namespace.Say_Other_Things(100,Hello);  -- still a method, but can't use Object.Method syntax
end Test;

Coming from Java, you might find it strange to have to use the object as first parameter to be able to use the object.method syntax - but it’s not that uncommon in other languages. For example, in Python you have to provide “this” as first parameter of methods, too.

I don’t understand less readable here. I find it more readable. I almost never hit the line limit either with vertical one parameter per line etcetera. Similar to the Flutter ui toolkit.

Often it just goes from Timer_Start to Timer.Start too.

This is a topic that I am, in no way, interested to start a discussion about.
Perhaps that is a matter of taste and of what one is used too?

Fair enough.
Not a big deal.

It is the same in Matlab, even though you do declare methods inside the class definition.

@Roald I tend to follow this design pattern when doing OOP in Ada: A design pattern for OOP in Ada | The AdaCore Blog

You might find it closer to what you know from Java.

2 Likes