100 Languages Speedrun: Episode 09: Ada
Tags: ada, programming
Published: 2021-11-29 17:14:07

Ada is an old programming language created to enforce "safe" programming.

It is best known for tiny accident when such a "safe" Ada program blew up Ariane 5 rocket in 1996, costing approximately $370m.

If we take ratio of losses due to programming language issues to number of people coding in a given language, Ada would likely rank as the absolute least safe of all languages. Sure, SQL injections cost $billions, but SQL is used by 10000x as many people as Ada, so an average SQL programmer is doing a lot less damage than an average Ada programmer. If that doesn't prove that TDD is more important than "static safety", then I don't know what will.

Also, as an old language Ada is fairly difficult to run on modern operating systems, so we'll be using Docker to run it.

It is named after Ada Lovelace, who is often called a "first programmer", [which is a considerable exaggeration](https://twobithistory.org/2018/08/18/ada-lovelace-note-g.html), but let's leave all that historical stuff behind and explore the language itself.

### Hello, World!

Let's start with `hello_world.adb`:

```ada
with Ada.Text_IO;
procedure Hello_World is
begin
  Ada.Text_IO.Put_Line("Hello, World!");
end Hello_World;
```

As I couldn't find any compiler running on OSX, we'll run it with Docker:

```
$ docker run --rm -i -t -v $(pwd):/source nacyot/ada-gnat:apt gnatmake hello_world.adb
gcc-4.6 -c hello_world.adb
gnatbind -x hello_world.ali
gnatlink hello_world.ali
$ docker run --rm -i -t -v $(pwd):/source nacyot/ada-gnat:apt ./hello_world
Hello, World!
```

One thing that's already notable is `end Hello_World`. Yes, Ada, for "safety" reasons, requires every `end` to specify what it's ending. I know it sounds like a joke, but people who created Ada were absolutely serious about it. In some cases you're allowed to skip saying what you're ending, but we're trying to write Ada the way it was meant to be written.

### Fibonacci

Here's Fibonacci in Ada:

```ada
with Ada.Text_IO;

procedure Fibonacci is

  function Fib(I: Integer) return Integer is
  begin
    if I <= 2 then
      return 1;
    else
      return Fib(I-1) + Fib(I-2);
    end if;
  end Fib;

begin
  for I in 1..20 loop
    Ada.Text_IO.Put_Line(Integer'Image(Fib(I)));
  end loop;
end Fibonacci;
```

Let's run it:

```
$ docker run --rm -i -t -v $(pwd):/source nacyot/ada-gnat:apt gnatmake fibonacci.adb
gcc-4.6 -c fibonacci.adb
gnatbind -x fibonacci.ali
gnatlink fibonacci.ali
$ docker run --rm -i -t -v $(pwd):/source nacyot/ada-gnat:apt ./fibonacci
 1
 1
 2
 3
 5
 8
 13
 21
 34
 55
 89
 144
 233
 377
 610
 987
 1597
 2584
 4181
 6765
```

There's a lot to unpack here:

* where's those extra spaces coming from? Ada thinks it's unsafe to convert `7` to `"7"`. In some weird idea of consistently since `-7` converts to `-7`, 7 converts to ` 7` with extra space where plus/minus sign would go.
* at least Ada has loops over ranges like `for I in 1..20 loop`, it's baffling people make languages even today which don't have that
* `Integer'Image` syntax for function in a package looks really weird, especially since we also have more familiar `.` on the same line.

### FizzBuzz

And finally we get to the FizzBuzz, and it's surprisingly convoluted in Ada. We need to strip that extra space. Well, surely Ada would have strings in its standard library, right? You wish. It its drive for "safety", it provides fixed-width strings only.

So you can do this:

```ada
S: String := "one";
S := "two";
```

But not this:

```ada
S: String := "one";
S := "three";
```

Or even this:

```ada
S: String := "three";
S := "one";
```

Nope, it's not maximum string length (like with SQL `varchar` columns), it must literally be exactly the same length.

Fortunately while local variables are crazy like that, function arguments aren't so we're finally able to write a FizzBuzz:

```ada
with Ada.Text_IO;

procedure FizzBuzz is
  function LStrip(S: String) return String is
  begin
    if S(S'First) = ' ' then
      return S(S'First+1 .. S'Last);
    else
      return S;
    end if;
  end LStrip;

begin
  for I in 1..100 loop
    if I mod 3 = 0 and I mod 5 = 0 then
      Ada.Text_IO.Put_Line("FizzBuzz");
    elsif I mod 3 = 0 then
      Ada.Text_IO.Put_Line("Fizz");
    elsif I mod 5 = 0 then
      Ada.Text_IO.Put_Line("Buzz");
    else
      Ada.Text_IO.Put_Line(LStrip(Integer'Image(I)));
    end if;
  end loop;
end FizzBuzz;
```

I'll skip all the minor weirdness like `=` for equality check, `()` for substring, indexing starting from 1, `S'First` and `S'Last`, and so on.

### Should you use Ada?

Only if you're trying to sabotage Blue Origin. Otherwise, no.

If you're really exceedingly safety-obsessed, there's Rust for you.

Otherwise, just about any modern properly memory-managed language (so all except C and C++) with decent TDD will do just fine.

### Code

[All code examples for the series will be in this repository](https://github.com/taw/100-languages-speedrun).

[Code for the Ada episode is available here](https://github.com/taw/100-languages-speedrun/tree/master/episode-09-ada).