Ada for the curious

For developers curious about Ada

This post is for developers who are curious about Ada.

Contrary to some associations you might have (πŸ¦•?), Ada is a modern language, with modules, a strong type system, and easy-to-use concurrency. It’s blazing fast (πŸš€!), backed by a small but friendly community, and it’s easy to learn.

Instead of giving an extensive introduction to syntax you can easily look up yourself, I want to highlight some of the aspects that make Ada worth looking into. Consider it a teaser that gets you to know Ada enough to decide whether you like it or not, and provides pointers where to explore further if you do.

Let’s start with a countdown:

-- countdown.adb

with Ada.Text_IO; use Ada.Text_IO;

procedure Countdown is
begin
   for I in reverse 1 .. 10 loop
      Put_Line (Integer'Image (I));
   end loop;

   Put_Line ("Lift off!");
end Countdown;

Ada might look unfamiliar if you grew up with C-family languages, because its syntax was based on Pascal. But it’s a classical procedural language.

So what’s special about it?

What makes Ada different

The development of Ada was originally sponsored by the US Department of Defense, when they noticed they spend way too much money on building and fixing embedded systems. (Here's how I imagine it must have started.)History of Ada

Since the first version in 1983, the language has developed continuously and far beyond embedded systems, but the most important point to understand about Ada’s origin is that:

Ada was designed specifically for safety-critical software.

More than anything else, this has shaped who Ada is.

You notice it when looking at the main application areas of Ada: aircrafts, ships, railway control, medical devices, defense, space. But you also notice it as a programmer using Ada.

Ada wants you to produce correct programs. Therefore:

  • Ada wants to make it easy for you to properly engineer a solution.
  • Ada wants to make it easy for you to express your intent clearly - both towards the compiler and towards your fellow humans.
  • Ada wants to make it hard for you to make errors.

As a consequence, Ada is easy to understand and easy to get right.

I let you judge yourself. For me, personally, Ada feels very different in a subtle way. And that’s because the language is working with you, and not against you. It gently forces you to design when you code. And it provides safety nets everywhere.

Of course this comes at a cost. It means that Ada is not ideal for fast prototyping or for dynamic meta-programming. Ada was made to write well-designed, solid code, that works and that can still be read when coming back to it after years.

That’s where Ada shines.

But that doesn’t mean you can’t use it for small fun projects.

If you prefer designing over debugging, I bet you will enjoy Ada.

What makes Ada interesting

Here are a few characteristics of Ada that many people like about it. This is subjective, of course, but it gives you a taste of why Ada might be worthwhile exloring.

The type system

One of the nicest safety nets that Ada offers is range restrictions on the type level.

Let’s assume you deal with temperature readings. Instead of using a generic floating-point type and check whether the values you get are in your expected range, Ada lets you define a custom type as having floating-point values within a specific range - and the compiler and runtime check this range for you. For example:

type Temperature_Celcius is digits 9 range -273.15 .. 300_000_000.0;
type Temperature_Kelvin  is digits 9 range 0.0 .. 300_000_000.0;

Any values below -𝟸𝟽𝟹.𝟷𝟻 degrees Celcius or 0 Kelvin are not a valid temperature. (If you are not building a fusion reactor, your accepted range might be much smaller, of course.)

Or:

type Latitude  is new Float range 0.0 .. 360.0;
type Longitude is new Float range 0.0 .. 360.0;

This is not only incredibly handy, it also makes the range restrictions very clear to anyone working with your code.

Range restrictions in types are, in fact, helpful even if you don’t want to restrict the data range. Consider the following neat trick. You can declaring a custom floating-point type like this:

type Some_Float is new Float range Float'Range;

This means your derived type has the same range as Float, but excluding anything that is not in its range: NaN, infinity, or whatever non-numeric values your machine defines. So if your code ends up with something that is not a number, numeric operations raise a constraint error instead of propagating the non-numeric value through your whole program.

Array indices

One of the good ideas that Ada adopted from Pascal is the fact that array indices can come from any enumerable, bounded type. So it doesn’t matter whether you believe array indices should start with 0 or 1. You define how they start. And whether they use integers at all.

It actually means you get arrays and maps in one datatype, which feels very natural once you have it. When defining an array type, you simply specify the index type as well as the value type.

Here is an example where we use integers within a specific range as index:

type Position is range 100 .. 999;
type State is (Open, Closed, Unknown);

type Valves is array (Position) of State;

Example : Valves := (
   100    => Open,
   101    => Open,
   102    => Closed,
   103    => Open,
   others => Unknown
);

Attributes like 'First, 'Last, and 'Range make it possible to define loops in a way that’s hard to get wrong even if you change the underlying index range later.

for Pos in Position'First .. Position'Last loop
   ...
end loop;

-- This is equivalent to:
for Pos in Positions'Range loop
   ...
end loop;

Most importantly, arrays are memory-safe. The index type and range is part of the array, and both compiler and runtime check that all reads and writes are within the bounds of the array. (We return this below in Memory safety and formal verification.)

Separation of concerns

Like separating header and implementation in C, Ada separates packages into a specification (.ads) and a body (.adb), which tends to blend design and code. This approach is not specific to Ada, of course, but it emphasizes Ada’s focus on engineering software. Richard Riehle put it like this:

Ada as an engineering tool, requires the software developers to adopt an engineering attitude to using it. It is not enough to simply be a good computer programmer when human safety is at risk. Software at that level of risk must be engineered.

If you have a specification file with proper comments, this can also serve as a very helpful documentation of a library. You can check the .ads files in the gnatcoll-core repository for examples.

Memory safety and formal verification

CVE’s Top 25 Most Dangerous Software Weaknesses reveals many recurring issues - with memory management errors still high on the list: out-of-bounds read and write, buffer overflows, null pointer dereferencing, use-after-free, and so forth. Both Microsoft and Google Chrome have confirmed that approximately 70% of the vulnerabilities they address stem from insecure memory handling.

Ada is memory safe. And it goes beyond compiler checks and promises.

It offers a formally verifiable subset, SPARK, which allows for mathematical proofs of correctness, covering things like the absence of out-of-bound access in arrays (statically proofing it at compile time, not only promising that it will raise an exception at runtime) and functional correctness of algorithms. And all of this with built-in tooling, which is really almost as easy as adding with SPARK_Mode. Welcome to proof-driven development.

And this focus on safety extends to concurrent and real-time programming, which have been part of the Ada standard since its beginning. Their semantics is the same independent of whether you execute them on Linux or an RTOS, and it’s as deterministic and safe as you can get. Read more about it here.

And tasking is not very complicated or hard to grasp - which lowers the risk of making mistakes and which brings me to the last point I want to mention: simplicity.

Simplicity

There is a very nice interview with Niklaus Wirth (the Swiss computer scientist who invented Pascal), where he remembers that during his time, all existing programming languages were unneccessarily complex. He wanted to design a language that is as simple as possible without losing power.

In Ada, you see some of this legacy.

The ecosystem and community

The Ada ecosystem can be a bit confusing in the beginning. But the core is simple: Ada is a free language. It’s an ISO standard and not owned by any company.

There are free and proprietary compilers. The one you will come across first and most often is called GNAT, a free Ada compiler integrated into GCC.

Ada has a small but friendly and welcoming community.

Forum: https://forum.ada-lang.io/

Discord: https://discord.gg/edM2czVKN4

Reddit: https://www.reddit.com/r/ada

There is also a monthly meetup, usually at the beginning of each month, which is accounced in the forum and on Discord.

Resources to learn Ada

Similar to this post, David Given has written an overview of Ada features called A random walk through Ada.

But if you want to get your hands wet instead, there are two entry points I would recommend.

AdaCore, a driving force behind many current developments in Ada, has a collection of excellent tutorials for people from different perspectives, which allow you to play with Ada directly in the browser. I highly recommend these tutorials as starting point.

The next great entry point, when you want to build and run Ada on your own machine, is ada-lang.io. It provides not only resources but also the package manager Alire, built by the community exactly for people like you.

Starting can be as easy as:

  • Download Alire.
  • Select the default toolchain.
  • Create a new repository.
  • Build and run it.

Once you want to know the language in more depth, there is no way around Programming in Ada 2012 by John Barnes, and the Ada Reference Manual (ARM) (or a prettier version of it on ada-lang.io). Both are very extensive and quite accessible, but feel free to not worry about them in the beginning - even though people will probably point you to both if you ask for pointers.

To explore the rest of the iceberg, Awesome Ada provides a pretty comprehensive list of resources.

If you want to dive into existing Ada code bases, here are a few suggestions:

Ada or Rust?

Here are several pointers for looking into comparisons:

Who is using Ada?

You can find some of the companies using Ada when checking the list of customers of AdaCore. This list is certainly not complete, but the picture is pretty representative. Companies range from big names, like Thales, Airbus, and the automotive team at NVIDIA, to start-ups you probably never heard of, like Latence Tech.