100 Languages Speedrun: Episode 98: Rexx
Tags: rexx
Published: 2022-02-19 23:00:06

Rexx is a language from the olden days, vaguely similar to Python, or Tcl, but doing a lot of things in a weird way because back then people didn't know better.

Rexx saw modest use back in the 1980s, and while it never got big, it still survives somewhat in the IBM mainframe world. You can install an Open Source Rexx interpreter with `brew install regina-rexx`.

### Hello, World!

I'm a bit surprised it uses `say` for `print`, I thought it's a new thing Raku did, but apparently it has much deeper roots.

```
#!/usr/bin/env rexx

say "Hello, World!"
```

```
$ ./hello.rexx
Hello, World!
```

### Variables

Rexx is case-insensitive. Variables by default contain their name, in upper case, so variable `world` starts with `WORLD`.

```
#!/usr/bin/env rexx

a = 40
b = 19

say a + b * 20
say A B C
say "Hello, " || world || "!"
```

```
./variables.rexx
420
40 19 C
Hello, WORLD!
```

This is definitely not the direction where the world of programming ended up going. `||` is string concatenation.

### Types

Everything is a string. If we use a string in numeric context, and it looks like a number, it's treated as a number. Otherwise, it raises exception:

```
#!/usr/bin/env rexx

say "2" + "67"
say "hello" + "world"
```

```
$ ./types.rexx
69
     4 +++ say "hello" + "world"
Error 41 running "types.rexx", line 4: Bad arithmetic conversion
```

### FizzBuzz

In a baffling reversal of the usual convention, `/` is float division, `%` is integer division, and `//` is modulo.

```
#!/usr/bin/env rexx

do i = 1 to 100
  if i // 15 == 0 then
    say "FizzBuzz"
  else if i // 5 == 0 then
    say "Buzz"
  else if i // 3 == 0 then
    say "Fizz"
  else
    say i
end
```

### Fibonacci

Let's do an easy Fibonacci sequence:

```
#!/usr/bin/env rexx

do i = 1 to 20
  say "fib(" || i || ")=" || fib(i)
end

fib:
  parse arg n
  if n <= 2 then
    return 1
  else
    return fib(n - 1) + fib(n - 2)
```

Something's not right here...

```
$ ./fib.rexx
fib(1)=1
fib(2)=1
fib(3)=2
fib(4)=3
fib(5)=4
fib(6)=5
fib(7)=6
fib(8)=7
fib(9)=8
fib(10)=9
fib(11)=10
fib(12)=11
fib(13)=12
fib(14)=13
fib(15)=14
fib(16)=15
fib(17)=16
fib(18)=17
fib(19)=18
fib(20)=19
```

Rexx by default makes everything global, so that `n` is getting overwritten by recursive calls, and crazy things happen.

Let's do a second try:

```
$ ./fib2.rexx
fib(1)=1
fib(2)=1
fib(3)=2
fib(4)=3
fib(5)=5
fib(6)=8
fib(7)=13
fib(8)=21
fib(9)=34
fib(10)=55
fib(11)=89
fib(12)=144
fib(13)=233
fib(14)=377
fib(15)=610
fib(16)=987
fib(17)=1597
fib(18)=2584
fib(19)=4181
fib(20)=6765
     7 +++ procedure
Error 17 running "fib2.rexx", line 7: Unexpected PROCEDURE
Error 17.1: PROCEDURE is valid only when it is the first instruction executed after an internal CALL or function invocation
```

That was closer, but why did it suddenly crash? Well, what if we add an `exit`?

```
#!/usr/bin/env rexx

do i = 1 to 20
  say "fib(" || i || ")=" || fib(i)
end

exit

fib: procedure
  parse arg n
  if n <= 2 then
    return 1
  else
    return fib(n - 1) + fib(n - 2)
```

```
$ ./fib3.rexx
fib(1)=1
fib(2)=1
fib(3)=2
fib(4)=3
fib(5)=5
fib(6)=8
fib(7)=13
fib(8)=21
fib(9)=34
fib(10)=55
fib(11)=89
fib(12)=144
fib(13)=233
fib(14)=377
fib(15)=610
fib(16)=987
fib(17)=1597
fib(18)=2584
fib(19)=4181
fib(20)=6765
```

Now it works. There's also a few different versions of `parse arg` with somewhat different semantics.

Of course, we can work with all this, but it's annoying such a basic thing is already causing issues.

### Arrays and Hashes

There aren't any. But we get a weird alternative instead - composite variables.

Let's try to implement very simple records this way:

```
#!/usr/bin/env rexx

a.name = "Alice"
a.surname = "Smith"
a.age = 25

b.name = "Bob"
b.surname = "Nilson"
b.age = 30

c.name = "Charlie"

call print_person a
call print_person "B"
call print_person c

exit

print_person:
  person = arg(1)
  name_ = value(person || ".name")
  surname_ = value(person || ".surname")
  age_ = value(person || ".age")
  say name_ || " " || surname_ || " is " || age_ || " years old"
```

```
$ ./person.rexx
Alice Smith is 25 years old
Bob Nilson is 30 years old
Charlie C.SURNAME is C.AGE years old
```

There's a lot going on here:

* there's separate syntax for function calls which must return values (`foo(a,b)`) - and procedure calls which don't (`call foo a b`)
* we cannot actually pass composite variable as argument, what we're passing is their names `A`, `B`, `C`
* value of `a` (by default `A`) is completely unrelated to what's in `a.name` etc.
* `call print_person b` is just `call print_person "B"` - as that's the default value
* we cannot mark `print_person` as `procedure`, because it needs full access to variables in the caller
* to get actual values we need `value` or `interpret` - that is essentially an `eval`
* default fields are uppercase of the whole thing so Charlie's default surname is `C.SURNAME` not even just `SURNAME`
* because `print_person` doesn't have its own local variables (`name_` etc. are all global) we need to be really careful here

Compared to languages which came after, this is all unbelievably primitive. I don't know if any other language tries to take this "composite variables" concept somewhere good, this definitely isn't it.

### Composite Arrays

OK, so given all the limitations of composite arrays, how do we even do anything with them?

The first problem is that we cannot even know the length, and we cannot iterate to see when array ends as accessing beyond its end will give us prefilled `ARRAY.7`, `ARRAY.8` etc.

So by convention we put the length of the array in `ARRAY.0`, and then its elements in `ARRAY.1`, `ARRAY.2`, etc. You can program this way, but it is quite ugly:

```
#!/usr/bin/env rexx

a.0 = 20

do i = 1 to 20
  a.i = 2 * i
end

b.0 = 3

do i = 1 to 3
  b.i = 21 + i
end

say sum(a)
say sum(b)
exit

sum:
  array_name = arg(1)
  array_size = value(array_name || ".0")
  result = 0
  do i = 1 to array_size
    result = result + value(array_name || "." || i)
  end
  return result
```

```
$ ./sum.rexx
420
69
```

### Should you use Rexx?

Not unless you're a time traveler who needs to go back to the 1980s to code on an IBM mainframe. Even then, I'm not sure it was such a great choice.

Rexx seems like a dead-end language. I don't think it had any influence on the languages which followed it.

### Code

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

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