Programming Ada: First Steps On The Desktop

Who doesn’t want to use a programming language that is designed to be reliable, straightforward to learn and also happens to be certified for everything from avionics to rockets and ICBMs? Despite Ada’s strong roots and impressive legacy, it has the reputation among the average hobbyist of being ‘complicated’ and ‘obscure’, yet this couldn’t be further from the truth, as previously explained. In fact, anyone who has some or even no programming experience can learn Ada, as the very premise of Ada is that it removes complexity and ambiguity from programming.

In this first part of a series, we will be looking at getting up and running with a basic desktop development environment on Windows and Linux, and run through some Ada code that gets one familiarized with the syntax and basic principles of the Ada syntax. As for the used Ada version, we will be targeting Ada 2012, as the newer Ada 2022 standard was only just approved in 2023 and doesn’t change anything significant for our purposes.

Toolchain Things

The go-to Ada toolchain for those who aren’t into shelling out big amounts of money for proprietary, certified and very expensive Ada toolchains is GNAT, which at one point in time stood for the GNU NYU Ada Translator. This was the result of the United States Air Force awarding the New York University (NYU) a contract in 1992 for a free Ada compiler. The result of this was the GNAT toolchain, which per the stipulations in the contract would be licensed under the GNU GPL and its copyright assigned to the Free Software Foundation. The commercially supported (by AdaCore) version of GNAT is called GNAT Pro.

Obtaining a copy of GNAT is very easy if you’re on a common Linux distro, with the package gnat for Debian-based distros and gcc-ada if you’re Arch-based. For Windows you can either download the AdaCore GNAT Community Edition, or if you use MSYS2, you can use its package manager to install the mingw-w64-ucrt-x86_64-gcc-ada package for e.g. the new ucrt64 environment. My personal preference on Windows is the MSYS2 method, as this also provides a Unix-style shell and tools, making cross-platform development that much easier. This is also the environment that will be assumed throughout the article.

Hello Ada

The most important part of any application is its entry point, as this determines where the execution starts. Most languages have some kind of fixed name for this, such as main, but in Ada you are free to name the entry point whatever you want, e.g.:

with Ada.Text_IO;
procedure Greet is
begin
    -- Print "Hello, World!" to the screen
    Ada.Text_IO.Put_Line ("Hello, World!");
end Greet;

Here the entry point is the Greet procedure, because it’s the only procedure or function in the code. The difference between a procedure and a function is that only the latter returns a value, while the former returns nothing (similar to void in C and C++). Comments start with two dashes, and packages are imported using the with statement. In this case we want the Ada.Text_IO package, as it contains the standard output routines like Put_Line. Note that since Ada is case-insensitive, we can type all of those names in lower-case as well.

Also noticeable might be the avoidance of any symbols where an English word can be used, such as the use of is, begin and end rather than curly brackets. When closing a block with end, this is post-fixed with the name of the function or procedure, or the control structure that’s being closed (e.g. an if/else block or loop). This will be expanded upon later in the series. Finally, much like in C and C++ lines end with a semicolon.

For a reference of the syntax and much more, AdaCore has an online reference as well as a number of freely downloadable books, which include a comparison with Java and C++. The Ada Language Reference Manual (LRM) is also freely available.

Compile And Run

To compile the simple sample code above, we need to get it into a source file, which we’ll call greet.adb. The standard extensions with the GNAT toolchain are .adb for the implementation (body) and .ads for the specification (somewhat like a C++ header file). It’s good practice to use the same file name as the main package or entry point name (unit name) for the file name. It will work if not matched, but you will get a warning depending on the toolchain configuration.

Unlike in C and C++, Ada code isn’t just compiled and linked, but also has an intermediate binding step, because the toolchain fully determines the packages, dependencies, and other elements within the project before assembling the compiled code into a binary.

An important factor here is also that Ada does not work with a preprocessor, and specification files aren’t copied into the file which references them with a with statement, but only takes note of the dependency during compilation. A nice benefit of this is that include guards are not necessary, and headaches with linking such as link order of objects and libraries are virtually eliminated. This does however come at the cost of dealing with the binder.

Although GNAT comes with individual tools for each of these steps, the gnatmake tool allows the developer to handle all of these steps in one go. Although some prefer to use the AdaCore-developed gprbuild, we will not be using this as it adds complexity that is rarely helpful. To use gnatmake to compile the example code, we use a Makefile which produces the following output:

mkdir -p bin
mkdir -p obj
gnatmake -o bin/hello_world greet.adb -D obj/
gcc -c -o obj\greet.o greet.adb
gnatbind -aOobj -x obj\greet.ali
gnatlink obj\greet.ali -o bin/hello_world.exe

Although we just called gnatmake, the compilation, binding and linking steps were all executed subsequently, resulting in our extremely sophisticated Hello World application.

For reference, the Makefile used with the example is the following:

GNATMAKE = gnatmake
MAKEDIR = mkdir -p
RM = rm -f

BIN_OUTPUT := hello_world
ADAFLAGS := -D obj/

SOURCES := greet.adb

all: makedir build

build:
	$(GNATMAKE) -o bin/$(BIN_OUTPUT) $(SOURCES) $(ADAFLAGS)
	
makedir:
	$(MAKEDIR) bin
	$(MAKEDIR) obj

clean:
	rm -rf obj/
	rm -rf bin/
	
.PHONY: test src

Next Steps

Great, so now you have a working development environment for Ada with which you can build and run any code that you write. Naturally, the topic of code editors and IDEs is one can of flamewar that I won’t be cracking open here. As mentioned in my 2019 article, you can use AdaCore’s GNAT Programming Studio (GPS) for an integrated development environment experience, if that is your jam.

My own development environment is a loose constellation of Notepad++ on Windows, and Vim on Windows and elsewhere, with Bash and similar shells the environment for running the Ada toolchain in. If there is enough interest I’d be more than happy to take a look at other development environments as well in upcoming articles, so feel free to sound off in the comments.

For the next article I’ll be taking a more in-depth look at what it takes to write an Ada application that actually does something useful, using the preparatory steps of this article.

66 thoughts on “Programming Ada: First Steps On The Desktop

    1. Yayyyy! No more squiggly, uncountable braces scratching my eyes as I try to read the code! No more unmatched parenthesis! No more “hanging”else!

      C style syntax is a catastrophe. It is ugly and hard to read. It was made to be easy to parse by the relatively crude parses available at the time it was invented. It was not made to be easy for a human to read and understand.

      C is ugly. C++ uglier still. C# and Java aren’t any prettier, and javascript adds its own warts and idiocies.

      C, C++ hang in there by being a good tool when you need fast code. Java and C# are kept around for when you need good memory management and safety in code with C style syntax. I assume people use javascript out of otherwise repressed masochistic tendencies.

      —–
      Having to declare objects and deal with strong typing seems like a hassle when all you want to do is get something running. Once you get outside of “slap it together in a couple of days” range, though, you’ll begin to appreciate the advantages of strong typing.

      1. The Ada hello world (

        with Ada.Text_IO;
        procedure Greet is
        begin
        — Print “Hello, World!” to the screen
        Ada.Text_IO.Put_Line (“Hello, World!”);
        end Greet;
        ) is super ugly in my opinion. I mean, have you seen how to do that in Python?

        1. I mean Ada was created in the 70’s while python was created in the 90’s.

          I imagine Ada’s big benefit will be code execution speed due to compilation while being object oriented with readable syntax.

          Python may be easy to write but man is it slow as an interpreted language.

          This seems like it’s still a good middle ground.

        2. Please, Python? That takes 24 characters. In Vyxal we just use kH; that’s obviously superior. Even the verbose APL only takes 15 characters: ‘Hello World!’

          Ada is not optimized for writing short scripts. Ada is optimized for being super clear about what’s going on in a long program. Yes, you need to declare what packages you’re using at the start of the file. Yes, they don’t dump all their symbols into global space unless you use “use” as well as “with”. These are good software engineering practices. There’s arguments about them, and other details of Ada, but comparing Hello, World! tells you nothing about how go it is about doing its job.

      2. C/C++ is butt ugly, but it’s also compact when needed. It’s bad practice to have your code structure flow in two directions, but sometimes you just want to do a curly bracket one-liner.

      3. I have to disagree with you there.

        I haven’t developed in Ada but I am occasionally forced to maintain old VisualBasic code which also uses English words to define it’s loops and other groupings.

        Just because words are used instead of characters doesn’t mean you can’t still end up with complicated nesting where it’s hard to see where one part begins and another ends.

        And once you are used to the parenthesis of C-style languages I think they are easier to skim and so quicker to find the part you are looking for.

        The things is those begin / end or for / next English words are made of the same characters as other words. Your brain has to process more characters to find that pattern “begin” or “end” that you are looking for.

        Parenthesis on the other hand… I’ve never seen a language that allows them as part of an identifier or keyword so they stand out pretty well as groupings. The only other place you might see them is inside a string constant but not really all that often.

        What really does help a lot though… good indentation.

        1. If your nesting gets too deep then it’s time to refactor. Ada also has lots of alternative constructs for conditionals that are more concise and are just as readable.

    2. It’s not at all like Pascal even though it uses some of the same keywords. Pascal’s more C-like in that it uses statement blocks, delimited with begin..end where C uses {..}. (Also, Pascal uses a separator semicolon rather than a terminator semicolon, but that’s a rant for another time.) Ada uses discrete statements for the beginning and end of each control structure and a separator semicolon.

      So, Pascal would do:

      while i 0 do
      writeln(i)

      …or:

      while i 0 do
      begin
      writeln(i);
      i := i – 1
      end

      (note position of semicolons!), Ada does:

      while i 0 loop
      writeln(i);
      i := i – 1;
      end loop;

      People have already mentioned that this forces the beginning and end of blocks to match, which is way more helpful than you’d think, but it also avoids the missing-brace issue which both C and Pascal has.

      In general is a really nice and unjustly maligned language. Programming it feels a bit like using C++, but mostly designed by grown-ups. It’s got generics, although the modern C++ STL has left Ada’s way behind (the C++ STL is based on Ada’s). It’s got built-in seamless concurrency based on rendezvous blocks and message passing which is a joy to use. It’s got parameterised types. For embedded stuff its ability to precisely define the layout of structures is so good. It’s got contract-based preconditions and postconditions. Ada’s version of pointers is weird but very effective and hard to misuse, although you won’t need them as often as in C/C++ because it’s got pass-by-reference built-in.

      It has its downsides. It requires variable declaration and usage to be split, C89-style. It’s got an object-oriented system but I hate it and find it incredibly clunky to use. It desperately needs a garbage collector (the language supports one but nobody’s implemented it); dynamic memory allocation is deliberately painful. It’s got exceptions but they’re barely useful. The STL is eccentric and very wordy to use.

      But my biggest issue with it is that Ada’s case insensitive, which was a thing back when it was designed but very much isn’t a thing now, and developers who’ve been trained that THIS_IS_A_CONSTANT and ThisIsAType and thisIsAVariable will have a _really_ bad time, and that’s everybody nowadays.

      If you’re at all interested, a while ago — yikes, 2014 — I wrote up a tour of the language:

      http://cowlark.com/2014-04-27-ada/index.html

      Or go look at a multithreaded mandelbrot renderer to see how easy it is:

      https://ideone.com/a1ky4l

      1. Well, and then there is BASIC without even a single punctuation mark when not explicitly needed:

        While i > 0
        Print i
        i -= 1 ‘ Yes, some dialects support the shorthand
        Wend

        Or if you know where i starts:
        For i = j To 0 Step -1
        Print i
        Next i

        Actually a sad thing that BASIC more or less died out outside of the hobbyist gamedev market.

  1. Very interesting. I would like to see how you can run an Ada program on say an ESP32 if possible.

    I am decently familiar with C, C++ (enough to get done what I want), very familiar with python and VB and Ada seems like an interesting mix of all of them.

    1. Ada’s a tough learning curve because C/C++/Python all share operators and some syntax. You can preprocess C header files into Python definitions trivially because “(((4*5) & 0x1) << 2) != 0" means the exact same thing in both languages. (Yes, yes, feel free to say 'wait that's dangerous what about typing' yadda yadda yadda).

      I've always found syntax variations are easier to flip between than operator and number changes. Tabs, curly braces, semicolons: whatever. But you make me think "wait how do I write this hexadecimal literal in this language" and I'm pitching your language straight into the trash.

    2. You can generally compile to anything that gcc can target, but there are more standard library pieces that you (or someone) will have to create to support some of the things that are needed for things that the Ada language proper requires. You might be able to get around some of them if you limit yourself to a subset of the language.
      The same is true for C, but there are fewer things built into the C language proper (ie things that don’t require including header files to use). One notable exception is that for processor targets without floating point C compilers will generate calls to software floating point functions.

  2. I remember back in the early 1980s when I was working for the Navy sitting in a meeting with a group of software engineers about to launch ADA for use in the Navy. They showed me a document on 3 different ways to use comments in ADA and ask my opinion on which one I liked. They also gave me a “I love ADA” mug which I still have.

  3. I looked at Ada briefly in the late 80s (there weren’t any projects that needed it so I never really got fluent). It seemed like what happens if COBOL and Pascal got drunk and had a baby. On the other hand, Modula-2 always seemed a more natural outgrowth from Pascal. On the gripping hand, Digital’s VAX Pascal had enough extra features for string handling and such that it got called BASCAL (at times it and VAX BASIC came close to being indistinguishable)… but I did a fair amount of work there.

    1. Early in my carrier I was unfortunate enough to work on a steaming pile of a language called DataFlex.

      It was what happened when a COBOL and Basic got drunk and had a ‘tarded fetal alcohol syndrome baby.

      Ontopic. Ada: Pentagon programming language…You have to code in triplicate.

  4. Getting my CS degree back in the 80s, we had a ‘Programming Landscape’ class where we got introduced to different languages with ADA being one. Even bought a book on ADA at the time. That was my only experience with it, as Assembly, Pascal and C were the main languages that I’ve used in my career. Add Python now to that list as I use it all the time at work… and at home.

    As for C being ugly and hard to read … that is just a matter of discipline when writing it. Don’t try to be ‘smart’ by building fancy ‘one liners’. Maintainability down the road is key… not cleverness. All variable names should mean what they are used for (minimum length of 3 chars unless it makes sence like x, y, z) . Sane organization of code and NO tabs — use spaces. We standardized on a 3 space indent rule and braces must be in same column for easy eye matching which brace goes with which. Simple rules like this really helped for readability across the ‘C’ code base. We never understood why you’d put a brace at the end of a statement like they are trying to enforce with Rust now-a-days…

    Example:
    cnt = 0;
    while (cnt < 10)
    {
    if (lookup_table[cnt] == 1)
    {
    // do something
    }
    cnt++;
    }

        1. No it isn’t. It is very easy to see where a block starts and where it ends. I would even say easier than having BEGIN and END statements because curly braces stick out more. But I imagine having an end statement that explicitly states the name of the function would be of great help when merging code, to help the merge tool accurately match lines.

        2. Beauty is in the eye of the beholder. And the beholder 20 years from now is the one who gets to write your epitaph…

          … no matter which ‘when’ you visit with your time machine, “the oldsters were idiots!”
          And, jump a few decades, and the guys who complained the first time are now the oldster idiots.

          Pretty sure it’s inevitable.

    1. The levels of language design..
      1) Best in class
      “Hello world”

      2) Honourable mention
      say “Hello world”

      3) Yeah ok I guess
      echo “Hello world”
      print “Hello world”
      writeln “Hello world”

      4) Garbage
      Ada.Text_IO.Put_Line (“Hello, World!”);
      System.out.println(“Hello world”);

      And if your Hello world program takes more than 3 lines it’s time to rethink your life choices

      1. In your first example, you have defined a literal but you haven’t declared what should be done with it. The action appears to be implicit, which is horrible because it just means you must then override this default action if you want to do anything else – and that’s just kicking the can down the road.

        1. I find your assertion that default actions are “horrible” both bizarre and contradicted by every computer program I’ve ever used. You’ll notice it’s implicit I’m printing to standard output and not a file or printer in every example but one but that doesn’t seem to be a problem for you. Not should it.

          1. OK. Supposed you want to write to a file and the screen. You have to switch contexts before executing every print statement. Brevity at the cost of readability and maintainability is a bad tradeoff.

      2. Depends on what you want to build… If one’s objective is a three-line application then scripting and dynamic languages are great and Ada would just make his/her life hard. But if one is building a big and complex system, I think more formality is welcome so that the compiler can immediately detect as many problems as possible.

        1. It depends if you’re the one that has to do all that extra typing or not. Using begin/end instead of {} requires 4x the amount of typing for the rest of your life vs the 30 seconds it takes to learn what the {} means. There are times to sacrifice brevity for clarity but in the most used part of the language? Only a committee that doesn’t have to live with their decisions could think that’s a good idea.

          1. It isn’t about the typing, it is about the reading. You spend far more time reading code than writing it if you ever have to make changes. Code needs to be written in such a way that it is easy to read.

            Learning what {} means is trivial. Scanning for those scratchy little bastards in a screen full of code is hard on the eyes.

          2. “Scanning for those scratchy little bastards in a screen full of code is hard on the eyes.”

            This isn’t the 1950s, you’re not working with paper printouts. The language is intended to be parsed by a computer, if you have problems identifying brackets, get an editor or plugin that can do it for you.

            Part of the reasoning for using symbols is that they don’t reduce your ability to name things: you’re not going to name a variable “{Process” but you might want to name something “beginProcess.” Plus of course there’s the internationalization problem: “{” doesn’t mean something in any language, so it’s equally identifiable, whereas “begin” is English-centric.

            Although, to be honest, I’d have to say it’d be *really* interesting to have a language that’s not actually stored just in text, but with metadata and viewing profiles so that things like delimiters/etc. can be just be a profile you choose. (Yes, that might sound like a nightmare, but so plenty of programming languages sounded bad when they started too).

          3. As I said above, it isn’t hard on the eyes or ugly … IF the open and close braces are in the same column. Very easy to pick out matching braces. Some editors have a ‘match’ brace function. So when checking, if a matching brace isn’t in the same column, something is wrong. What is really ugly is the opening brace ‘way’ over there at end of statement and the closing brace way to the left. Not easy on the eyes nor easy to read. I don’t get why this ‘style’ is prevalent today. It is bad bad bad, oh, and really bad. Go is one language that actually ‘enforces’ this ugly style. Yuck.

  5. “Despite Ada’s strong roots and impressive legacy, it has the reputation among the average hobbyist of being ‘complicated’ and ‘obscure’, yet this couldn’t be further from the truth”

    Ironically, Ada is used by governments, which have a reputation for being ‘complicated and obscure’.

  6. “Who doesn’t want to use a programming language that is designed to be reliable, straightforward to learn and also happens to be certified for everything from avionics to rockets and ICBMs?”

    KSP should have used Ada.

  7. ” It seemed like what happens if COBOL and Pascal got drunk and had a baby” – exactly.

    I don’t think there is any reason why you would use ADA unless you had to by your employer (same with cobol). And they would have all been better using pascal even to this day, and if they wanted something better than pascal – go to rust.

    ADA should just become another one of those dead languages…

    1. We used what was basically Niklaus Wirth Pascal for our compiler class c. 1980 — the world’s worst string handling … or so I thought until I began programming professionally in C seven years later. (I never used more modern versions of Pascal, so I don’t know what string extensions were used in, for example, Turbo Pascal. C held its position as the worst until I did some Forth programming 20 years later and I ended up writing Forth words for some of the C Library string functions.) We used COBOL in our database class; it was not bad (all that environment stuff, etc. is optional) and I am apparently not alone in thinking COBOL was less wordy than Java.

      When programming in Forth (out of personal interest) in the late 2000s, I researched and read a number of papers from the Johns Hopkins University Applied Physics Lab (JHU/APL). From the 1980s up into the early 2000s, they frequently used Forth for their non-critical spacecraft flight software and Ada for the critical flight software. It’s a shame they didn’t realize they should have used Pascal. (It might have saved them from designing their own direct-execution Forth microprocessor; this was in addition to the Harris RTX-2000s they were already using.)

      In the mid-2000s, I worked on ground control systems for commercial communications satellites. One satellite had 10s of thousands of telemetry points defined for Ada and C++ variables, used when dumping onboard computer memory.

      I don’t know what languages are used for spacecraft flight software today and the faster CPUs and vastly increased RAM certainly make the use of newer languages possible.

  8. Better technologies available?

    Recently clicked on gcc c app using Raspberry Pi 4B to check portability from x86 to arm platforms.

    gedit always opened my apps in the past.

    For the first time geany opened my app!
    https://www.google.com/search?q=geany+ide&rlz=1C1QCTP_enUS1084US1084&oq=geany+id&gs_lcrp=EgZjaHJvbWUqBwgAEAAYgAQyBwgAEAAYgAQyBggBEEUYOTIHCAIQABiABDIHCAMQABiABDIHCAQQABiABDIHCAUQABiABDIHCAYQABiABDIHCAcQABiABDIHCAgQABiABDINCAkQABiGAxiABBiKBagCALACAA&sourceid=chrome&ie=UTF-8

  9. As one who has programmed in Ada for a living for half a decade, and still do occationally, I wanted to add a couple of useful pointers for those who want to try doing something real in Ada.

    1. To use the POSIX API you will need florist: https://github.com/AdaCore/florist
    2. As for small embedded work there is a GNAT for AVR: https://sourceforge.net/p/avr-ada

    I seem to remember that the AVR port lacks exception-support, but that may have changed in the years since i looked at it the last time.

    Happy Ada:ing!

  10. That link to GNAT Programming Studio is long dead, Wikipedia deemed it non-notable. The AdaCore page doesn’t even mention a community edition anymore, just ‘request pricing’ for Pro. Based on a reddit post, the thing to do is to download a recent continuous release from the gnatstudio github releases.

    The same reddit post also mentions Alire as being nearly mandatory, it’s the library retrieval and build tool, which sounds very much like the role Apache Maven fills for Java. Not clear if gnatmake fits in with that tooling at all, or is supplanted by it.

  11. What’s wrong with assembly it’s easy, you just have have the opcodes and instructions sheet for the CPU.

    I get it, lazy people don’t want literally bare metal everything, from bootloader to firmware/os, to garbage collection and memory management

    All from scratch

    But assembler is the best

    Because it’s effectively programming in binary

    Using hexadecimal nowadays

    No obfuscation between the code and hardware it runs on, the most efficient way of programming too if you take the time and effort to do it right

    You can read asm and compute exactly what the CPU will do by hand, just takes you longer.

    When u use assembly, and do it well

    You won’t have backdoors and software exploits if the code is done well

    Look at the Xbox 360…

    1. Bah, who needs your assembly, do it in machine code so you can control the addressing and layout directly.

      “You won’t have backdoors and software exploits if the code is done well”

      You don’t have backdoors and software exploits if *any* code is done well. That’s practically the definition of “code done well.”

    2. As much as i like coding in machine it has one huge problem: Portability

      If you code something that should only run on, lets say, the C64 then go ahead, grab ACME or ca65 and get cracking.
      But if you want your crap to run on anything, grab C.

    3. You sound like a typical inexperienced, and cocky beginner after successfully writing code to output a few characters to the screen. Assembly has few, if any, advantages on today’s complex microprocessors. Most modern compilers can produce much better code than humans. I suggest you learn more before embarrassing yourself.

  12. I’ve always wanted to give learning Ada a shot, it seems like an awesome language. But unfortunately the poor Windows support made me lose interest. All the tooling feels like the usual “we technically ported it to Windows so stop complaining” level of quality. I refuse to ever install MSYS2 again after what it did to my system last time, and I have no patience for using Linux anymore.

    1. Don’t give up. Alire is a great tool for setting up your environment. You might want to use Linux Subsystem for Windows. It’s a better environment and it’s better isolated from Windows than MSYS2.

    2. Ada works perfectly fine under Windows. No need to use a Linux like environment. Alire is available as a native windows tool for obtaining a native compiler. Win32 bindings are available from AdaCore on GIT. GTKAda is also available on Windows for GUI design.

    3. I just tested above with Msys2 on my Windows work computer. Worked fine. Never had it do ‘anything’ to any of the systems I installed it on… Just my experience. Note I use the environment more for C/C++ development and was actually surprised to see gnat installed.

      As for Linux, it is the cat’s meow. Solid, dependable, does everything I need a computer to do. Been using Linux since the 90s (when loaded from a set of floppies) and been Windows free at home for years now (last I owned was Windoze 7). No going back to M$ based systems — ever. No need.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.