Chapter 2. Getting started

In this chapter, we will give you a quick introduction to the Rax language and interpreter. You will write your first "hello world" program and meet the most important features of the language.

Rax can be used in both interactive and batch mode. To start the Rax interpreter in the interactive mode, simply type:

    rax
       

on your shell command line. Then you can start typing Rax commands which will get executed immediately after you hit ENTER, just like in shell. Typing an end-of-file character (Ctrl-D on Unix-like systems and Ctrl-Z on Windows) will cause the interpreter to exit. Another way of using the Rax interpreter is to put your Rax program in a file (script) and pass the file name as a command-line parameter to Rax:

    rax my-first-script.rax
       

Finally, you can run Rax from various third-party code editors, such as Notepad++ for Windows users[6] or Textadept for OS X and Linux users[7].

To execute your first Rax program, start the Rax interpreter and type:

    `print "Hello, world!";     // Output: Hello, World!
       

As you might have guessed, this little Rax program prints Hello world! on the screen. The back quote sigil signifies that `print is an output procedure. A procedure (i.e. a function that returns no value) is a somewhat exceptional creature in a functional language. In Rax, procedures are only used to produce output (either textual or graphical) or to export data . That's why we call them output procedures. Output procedure names in Rax always start with a back quote. Other than that, the little program above should look familiar to people with programming experience: like in C, Rax statements are terminated with a semicolon and strings are enclosed in double quotes. Like in C++, comments start with a double slash and are terminated by the end of the line.

Let's now take a look at Rax variables. Rax is a strongly typed language, so all variables have to be declared before they can be used. Like in C, type precedes the identifier in a variable declaration. Like in APL, type specifiers are symbols rather than keywords. In the following example, we will make friends with four basic types in Rax: a boolean, a number (an integer), a real (a floating-point number) and a string:

    ?: bool;                  // A boolean.
    #: myOwnNumber, s;        // Two 64-bit numbers.
    &: some'real;             // A 64-bit floating-point number.
    $: a_string;              // A String.
       

As you can see, Rax identifiers look like identifiers in many other programming languages: they consist of alphanumerical characters plus the underscore. The first character must be a letter. Unlike in most other languages, though, Rax allows identifiers to contain the ' character, and therefore some'real is a valid identifier in Rax. This allows things like op' := derive(op); where op' would be pronounced as "op prime."

Now that we've declared the variables, we can start using them to actually do some calculations:

    bool        := (2 + 2) == 4;
    myOwnNumber := (3+5)*2;      // White space optional.
    s           := 1;
    some'real   := sqrt:(sin:(s**2) + cos:(s**2));
    a_string    := "Hello" + ", " + "world!";
       

The first thing to notice in this example is the assign operator: :=. We chose the Pascal assign operator, rather than the C (=) assignment operator, to avoid the confusion between assignment and comparison that has driven many C programmers to madness. The comparison operator is ==, and the single equals sign, =, has no meaning in Rax. The `print output procedure can be used to display values of any type, as shown in the example below:

    `print bool;         // Output: true
    `print s * 10;       // Output: 10
    `print some'real;    // Output: 1.17548853277096
    ? a_string;          // Output: Hello, world!
       

Note that, like in many version of BASIC, a question mark can be used as a shorthand for `print. A word of caution though, using the ? shorthand is considered bad style and may lead to deduction of karma points.

Rax has a pretty standard set of arithmetical operators - most of them come directly from C. An exception in the above example is the power operator **, which is taken from Ada. Rax also has an extensive set of mathematical functions. For the full list of operators and functions see Chapter 3, Basic types.

As a functional language, Rax does not contain any control structures, such as loops or if statements. Similarly to APL, Rax contains many operations that are carried out on entire sets of values, which makes loop construct unnecessary. Instead of the if construct, Rax provides an immediate-if operator, similar to the one in the C programming language:

    #: sales_price := 120;

    `print sales_price >= 200 ? "Expensive!" : "Not too expensive.";

    // Output: Not too expensive.
       

Values can be translated into other values by nesting immediate-if operators. Repeating the value to be translated can cause some code bloat necessitating a creative code layout, as can be seen in the example code below:

    // Translate a sales price into a blurp using iif:
    //
    `print
      sales_price <  100 ? "That is cheap!" :
      sales_price == 150 ? "That is what we expected." :
      sales_price >= 200 ? "That is expensive!"
                         : "That is acceptable.";  // Default answer.

    // Output: That is acceptable.
       

In a functional language this is a common construct for translating values. For these kind of translations, Rax has a translate operator, similar to the immediate if, but it can translate a value into any number of other values (of the same type). It features multiple comparisons where the left-hand side of the comparison expression has been replaced by a ? place holder, see example below:

    // Translation table layout:
    //
    `print
      sales_price
        ? <   100: "That is cheap!"
        ? ==  150: "That is what we expected."
        ? >=  200: "That is expensive!"
        ?        : "That is acceptable.";  // Default answer.

    // Output: That is acceptable.

    &: a := -1.2;

    // Translation one-line layout with optional parenthesis:
    //
    `print a ?< 0:(-a) ?== 0:(1e-10) ?:(a); // Output: 1.2
       

Note that translation comparisons are simple two-argument boolean expressions. The translate operator does not take complex boolean expressions, like the immediate-if does. In fact, the only syntax allowed is a ? place holder followed by a: <, >, |<=, >=, ==, !=, <: (element of), :> (contains), =~ (regex match), !~ (regex not match), or : (default). The empty (i.e. default) boolean expression can only be used at the end of a translation expression.

Next to basic types like boolean, number, real, and string, Rax has also special temporal types so that you can easily express, and perform calculations on, temporal values. The following example illustrates the time, absolute duration, relative duration and interval types:

    @: point_in_time     := (@)"2014-09-05T15:10:00";
    ^: absolute_duration := (^)"P7W3DT1H5M";
    ~: relative_duration := (~)"P1Y4M";
    |: interval          := (|)"2014-09-05T15:00:00/PT1H20M";     // One way.
    |: interval'         := (|)[(~)"PT1H20M",(@)"20140905T1620"]; // Alternative.
    |: interval"         := |"20140905T15","PT1H20M"|;            // Alternative.
       

The first variable represents an exact point in time, like the 5th of September 2014, at 3:10 pm. You can construct a value of this type by casting a time string in ISO 8601 format[8] to time. The cast operator is the same as the cast operator in C: (type). The second and third variables represent durations. Rax has two types representing durations: absolute and relative. An absolute is a duration whose actual length does not depend on the starting point of the duration, for example: 10 days, 2 hours, 5 seconds. A relative is a duration whose actual length can only be computed when the duration is anchored in a specific point in time, for example a month (28-31 days) or a year (365 or 366 days). Having two separate types for different sorts of durations makes Rax code more intuitive and less error prone. As an example of using durations, let's calculate the age of one of the authors of this manual:

    // How old is Gosia?
    `print (@)"now" - (@)"1978-02-08";

    // Error: Invalid diff operator '-', instead use either '^' or '~'.
       

Oops, that returned an error. The reason is that Rax doesn't know in which duration we are interested: an absolute one (number of days and weeks) or a relative one (number of years and months). Luckily Rax gave us a hint to use either the ^ or the ~ operator. Also note, that the string "now" cast to a point in time results in the current time. So let us try again:

    // How old is Gosia?
    `print (@)"now" ^ (@)"1978-02-08";

    // Assuming "now" is "2015-01-01T12:33:22.5"
    // The output would be: P1925W1DT12H33M22.50000S
       

Hm, Gosia is 1908 weeks and 6 days old. That's probably correct, but that's not exactly what we were interested in. Except for very tiny babies, we're typically interested in people's age expressed in years. Since year is a relative duration, we should use:

    // How old is Gosia?
    `print (@)"now" ~ (@)"1978-02-08";

    // Assuming "now" is "2015-01-01T12:33:22.5"
    // The output would be: P36Y11M-6DT-11H-26M-37.50000S
       

That's correct, Gosia is 36 years old.

The last temporal type is the interval denoted by the | symbol, which can represent periods, such as 1 hour and 20 minutes, starting at 3pm on 5th of September 2014. Note that there are several ways to form an interval. All three intervals, interval, interval' (traditionally pronounced "interval prime"), and interval" ("interval double prime"), are set to the same values. The first is standard ISO 8601 notation casted, using (|), to a Rax interval. The second is a tuple with a relative duration and an end-date, also casted to an interval. Note that casting a tuple is a more flexible way to express an interval value. The tuple can contain begin and end data, or either one of them and a relative or absolute duration. The third and last interval value is a shorthand notation for the tuple cast where the temporal types are inferred from the string notation.

Besides the simple types that we have described so far, Rax has two ways to define a composite data type: the ordered set and the tuple. The former, like Smalltalk's OrderedCollection, allows a variable number of values of one type to be grouped. The latter, like the C struct would allow a fixed number of variables of different type to be grouped. Here are some examples:

    {#}: setOfNumbers;                             // An ordered set of numbers.
    [&,#]: tupleOfRealNumber;                      // A real and number combined.
    [$:Reg, $:Ron]: KrayTwins;                     // Two labeled strings combined.
       

The examples are pretty straightforward: a set of numbers, a tuple of a real and a integer number, and a tuple of two string fields labeled Reg and Ron. Rax provides literal compound values in the same straightforward fashion:

    setOfNumbers    := {1,2,3,4};
    tupleRealNumber := [3.14, 42];
    KrayTwins       := ["UNICOS/mk 1", "UNICOS/mk 2"];
       

Note that neither tuple type definitions nor literal tuples need labels, but when supplied, they are useful when individual elements or fields have to be addressed within a composite data type. Below are some examples of addressing an individual element or field:

    `print setOfNumbers[2];        // Output: 2
    `print tupleRealNumber.#1;     // Output: 3.14
    `print KrayTwins.Ron;          // Output: UNICOS/mk 2
       

These would give you, in turn, the second number of the setOfNumbers (according to this set's current ordering, see Chapter 8, Ordered sets, the section called “Set ordering”), the real component of the tupleRealNumber, and the second of the KrayTwins. As expected, composite types can be used to create (even more) composite types. Here are two examples of a valid Rax type definitions:

    {[#:id, $:name, #:age]}: tablePeople;         // A table of numbers and strings.
    {[{[[{[{#},$:s]},&:r],&:r]}:s,$:s]}: aMess;   // A more complex table.
       

The first is a set of tuples containing and numeric id, a name and an age. Throughout this book, a set of tuples often will be referred to as a table. However, unlike SQL, they are not the only way to create a composite type as demonstrated by the second example. The second definition is a set of a tuples containing a set and a string, both named s. The inner set in turn holds a tuple, holding a tuple and a real named r and so on. Tuple fields can be addressed both by name (label) as well as by index. Moreover, if one tuple holds multiple fields with the same name, those fields van be addressed with a name-index pair. Literal tuples can also feature labels as demonstrated by the code below:

    // Three ways to address the first fields labeled ''n'':
    `print [3:n, "T":s, .43, "X":n, 9:s, 3.5:n].n;   // Output: 3
    `print [3:n, "T":s, .43, "X":n, 9:s, 3.5:n].n#1; // Output: 3
    `print [3:n, "T":s, .43, "X":n, 9:s, 3.5:n].#1;  // Output: 3

    // Two ways to address the second fields labeled ''n'':
    `print [3:n, "T":s, .43, "X":n, 9:s, 3.5:n].n#2; // Output: X
    `print [3:n, "T":s, .43, "X":n, 9:s, 3.5:n].#4;  // Output: X

    // One way to address unlabeled fields:
    `print [3:n, "T":s, .43, "X":n, 9:s, 3.5:n].#3;  // Output: 0.43
       

Rax sets differ from math sets in two ways. First, Rax sets can contain duplicates. In that sense, Rax sets are actually multisets. Second, Rax sets are ordered, which means that there is always a certain order attached to it (either implicitly or explicitly). More about set ordering in Chapter 8, Ordered sets, the section called “Set ordering”.

Rax has many operators defined on sets. They can be roughly divided into three categories: classical set-theory operators, such as union or intersection, relational algebra operators, such as projection or selection, and finally temporal relational operators that are unique to Rax, such as temporal cut. Let's take a look at a couple of examples:

    {#}: A := {1,2,2,3};
    {#}: B := {2,3,4};
    `print A \/ B;         // Output: {1,2,2,3,2,3,4}
    `print A /\ B;         // Output: {2,3}
    `print A \ B;          // Output: {1,2}
       

The code listing above illustrates the three basic set-theory operators: set union (\/), set intersection (/\) and set difference (\). Note, however, that handling of duplicates in Rax (multi)sets differs from handling duplicates in classical sets. For example, in classical set theory, A \/ B would equate {1,2,3,4}, because all duplicates would be eliminated. The next code listing will illustrate a couple of relational operators. The select operator allows to select only the set elements (or table rows in relational-algebra terminology) that meet a certain condition, for example people younger than 30. The project operator allows to select only certain fields (or columns in relational-algebra terminology) or to create new fields based on existing fields, for example, create a field containing the full name based on the first and the last name.

    {[$:first_name, $:last_name, #:age]}: people := {
      ["Gosia", "Wrzesinska", 25],
      ["Jan-Mark", "Wams", 60]
    };

    `print select [.age < 30] people;

    // Output:
    //: first_name|  last_name |age
    //: ----------|------------|---
    //:   "Gosia" |"Wrzesinska"| 25

    `print project[.first_name + " " + .last_name :fullname, .age] people;

    // Output:
    //:      fullname     |age
    //: ------------------|---
    //: "Gosia Wrzesinska"| 25
    //:   "Jan-Mark Wams" | 60
       

Note that the expressions inside the project operator can be labeled. In the example above, the expression .first_name + " " + .last_name has been labeled :fullname. It can be used to address the first field in the resulting table and it finds its way into the output of `print. Another important group of relational operators are joins that allow to combine multiple sets. Below an example of a simple natural join:

    {[$:name, #:dept_id]}: employees := {
      ["Gosia Wrzesinska", 1],
      ["Daniela Gavidia", 1],
      ["Matt Dobson", 2],
      ["Jan-Mark Wams", 3]
    };

    {[#:dept_id, $:dept_name]}: departments := {
      [1, "Software development"],
      [2, "Hardware maintenance"],
      [3, "Entertainment"]
    };

    `print employees |><| departments;

    // Output:
    //:        name       |dept_id|       dept_name
    //: ------------------|-------|----------------------
    //:  "Daniela Gavidia"|   1   |"Software development"
    //: "Gosia Wrzesinska"|   1   |"Software development"
    //:   "Jan-Mark Wams" |   3   |    "Entertainment"
    //:    "Matt Dobson"  |   2   |"Hardware maintenance"
       

In the SQL terminology, we joined tables employees and departments by matching the rows from both sides for which the value of the dept_id attribute is equal. Apart from the natural join, Rax provides many other join operators which are described in more detail in Chapter 8, Ordered sets, the section called “Relational operators”.

Next to traditional join operators, Rax provides temporal join operators that are unique to Rax. Let's take a look at an example, in which temporal and, denoted by @&@, is used.

    {[|:lunch, $:who]}: lunch_breaks := {
      [(|)"2000-08-20T12:00/PT1H",  "jan"],
      [(|)"2000-08-20T12:10/PT40M", "piet"],
      [(|)"2000-08-20T12:20/PT20M", "kees"]
    };

    {[$:who, |:meeting]}: meetings := {
      ["jan",  (|)"2000-08-20T12:05/PT15M"],
      ["jan",  (|)"2000-08-20T12:40/PT30M"],
      ["jan",  (|)"2000-08-20T15:40/PT15M"],
      ["kees", (|)"2000-08-20T12:15/PT30M"]
    };

    `print lunch_breaks @&@ :[.who#1 == .who#2] meetings;

    // Output:
    //:           lunch          |  who |  who
    //: -------------------------|------|------
    //: 2000-08-20T12:05:00/PT15M| "jan"| "jan"
    //: 2000-08-20T12:20:00/PT20M|"kees"|"kees"
    //: 2000-08-20T12:40:00/PT20M| "jan"| "jan"
       

In this example, we have two tables: one containing a list of people's lunch breaks and another one with a meetings schedule. We want to compute which (parts of) meetings took place during lunch breaks. The entire computation takes just one expression:

    lunch_breaks @&@ :[.who#1 == .who#2] meetings;
       

The temporal-and operator, @&@, looks for the first field of the type | (interval) in each set. In this case this is lunch in lunch_breaks and meeting in meetings. Next, a join is performed on both sets with two join conditions: first, the intervals have to overlap, and second, the condition in the extra conditions have to be met. The extra conditions follow the join operator, and are enclosed in :[ and ]. In this case, the extra conditionals are:

    :[.who#1 == .who#2]
       

which means that the person having the lunch and the meeting has to be the same. In the result of the join, the overlap between the two intervals is computed and returned instead of the two original intervals. In our case, the result of the join is:

              lunch          |  who |  who
    -------------------------|------|------
    2000-08-20T12:05:00/PT15M| "jan"| "jan"
    2000-08-20T12:20:00/PT20M|"kees"|"kees"
    2000-08-20T12:40:00/PT20M| "jan"| "jan"
       

This set describes parts of the meetings that took place during people's lunch breaks.

So far, all Rax sets we have seen were typed in element by element, or created using the range operator (.., for example: {1..10}). Obviously, this is not the only way of getting data in and out of Rax set. You can import data from a file, for example:

    {[#:respondent_id, #:channel_nr, |:viewing_period]}: viewing_data;
    viewing_data := <\viewing_data:"viewing_data.csv">;
       

This statement reads data from a CSV file "viewing_data.csv" and stores it into the set viewing_data. Since Rax is running on top of an SQL database, you can also map data that you have in an SQL table onto a Rax set:

    {[#:respondent_id, #:channel_nr, |:viewing_period]}: viewing_data;
    viewing_data :=
      import [(#)"RESPONDENT_ID", (#)"CHANNEL", (|)[(@)"BEGIN", (@)"END"]]
        "viewingsdb.T_VIEWING_DATA";
       

This statements maps the viewing_data set onto the RESPONDENT_ID, CHANNEL, BEGIN and END columns from the T_VIEWING_DATA table in the viewingsdb database. Note that two columns, "BEGIN" and "END", were mapped onto a single field viewing_period. SQL doesn't have an interval datatype, so intervals have to be described using two columns. Also note, that in order to map tables onto Rax sets, a database has to be located in the same database server as the database on top of which Rax is running.

Data can also be exported from Rax, either to a file or back to an SQL table. Rax also have graphical capabilities, allowing to easily plot data. You can read more about it in Chapter 11, Getting data in and out of Rax and Chapter 15, The graphical library.

This concludes our short tour of Rax. We touched on many important features of Rax, but many other, equally important features have not been mentioned. Keep reading, to discover the full power of Rax.



[6] For detailed instructions on how to integrate Rax with Notepad++ see Appendix A, Using Rax with Notepad++

[8] You can find more information on the ISO 8601 format here: http://en.wikipedia.org/wiki/ISO_8601