Chapter 14. Rax output

Rax is a functional language and therefore Rax statements are not allowed to change state. However, Rax, in order to tell us what actually calculated, it needs to change the state of the computer screen, otherwise it would be pretty useless. Therefore, Rax has a few statements, that are not in fact purely functional. We call them output procedures since they:

You are already familiar with the most important output procedure, namely `print, which accepts a Rax value of any type as a parameter and prints this value's textual representation to the screen:

    Rax 1 -> `print 1;
    1
    Rax 2 -> `print "alamakota";
    alamakota
    Rax 3 -> `print (@)"now";
    2014-10-08T16:45:01.98640
    Rax 4 -> {[#:id, $:name]}: words := {[1, "ala"], [2, "ma"], [3, "kota"]};
    Rax 5 -> `print words;
    id| name
    --|------
     1| "ala"
     2| "ma"
     3|"kota"
       

Since `print is used so often, Rax also provides a shortcut: `:

    Rax 1 -> ` 1;
    1
    Rax 2 -> ` "alamakota";
    alamakota
    Rax 3 -> ` (@)"now";
    2014-10-08T16:45:01.98640
       

Note that, unlike with functions, the arguments of output procedures are not enclosed in brackets.

Another important built-in procedure in Rax is `log. `log behaves the same as `print except that its output, which by default goes to standard output, can be redirected to a file, to standard error or even completely switched off. This can be done by means of %logfile meta instruction (for more info on meta instructions see Chapter 19, Meta instructions and macros). For example:

    `log "Hello world!";
       

Will simply print "Hello world!" on the screen, just as `print would do. However:

    %logfile "rax.output";
    `log "Hello world!";
       

Will print "Hello world!" to the rax.output file instead of to the screen. And:

    %logfile +"rax.output";
    `log "Hello world!";
       

Will append "Hello world!" to the rax.output file. Finally:

    %logfile off;
    `log "Hello world!";
       

Will switch off logging completely. The `log "Hello world!"; statement will not be executed. This makes `log very suitable for debugging output which can be switched on and off with a single meta instruction. To switch logging back on:

    %logfile stdout;
    `log "Hello world!";
       

Note that switching logging on or off can have a significant impact on the performance of your Rax scripts, since when logging is off, the `log statements are not evaluated. The example below shows a fragment of a Rax script which computes reach of a number of TV channels and logs intermediate results:

    %logfile off;

    // Add respondent weights to the exposures.
    {[#:RespondentId, |:Timeslot, #:ChannelId, &:Weight]}: TvExposuresWeighted :=
      project [.RespondentId#1, .Timeslot, .ChannelId, .Weight]
      (TvExposures @&@ :[.RespondentId#1 == .RespondentId#2]
         RespondentWeights);

    `log TvExposuresWeighted[1..6];

    // Compute the total viewing time (in minutes)
    // per respondent, per channel
    {[#:totalDuration, #:RespondentId, #:ChannelId, &:Weight]}: Durations :=
      fold [/sum(.#1), .#2, .#3, .#4]
      project [.Timeslot.absolute.minutes, .RespondentId, .ChannelId, .Weight]
      TvExposuresWeighted;

    `log Durations[1..6];

    // Compute the reach for each channel: sum of weights of respondents
    // watching this channel divided by the sum of weights of all respondents.

    &: TotalWeight :=
      fold [/sum(.#1)]
      fold [/mean(.Weight), .RespondentId]
      (RespondentWeights @&@ {[PeriodOfInterest]});

    // Compute reach per channel
    {[&:Reach, #:ChannelId]}: ReachPerChannel :=
      project [.#1/TotalWeight, .#2]
      fold [/sum(.Weight), .ChannelId] Durations;

    // Print out the results
    `print "Reach per channel:";
    `print "==========================================================";
    `print ReachPerChannel;
    `print "==========================================================";
       

Rax, since it is a functional language, uses lazy evaluation which means that the statements are not evaluated until it's absolutely necessary, that is, when the Rax user actually wants to see the result. In the above example, when logging is off, the statements in this script will be only evaluated when the `print ReachPerChannel is reached. This gives Rax an opportunity to optimize. In fact, all the set transformations in this example are folded and translated into just two SQL statements. When the logging is switched on, the set transformations have to be evaluated one by one to produce the intermediate results. This causes the execution time of this particular script to increase tenfold. You can find out more about how Rax interacts with the underlying SQL database in Chapter 21, Rax SQL back ends.

The third built-in output procedure in Rax is `export which we have already met in Chapter 11, Getting data in and out of Rax.

Except for the built-in output procedures, more output procedures can be created by users either in Rax or as an external function (for more info on external functions, see: Chapter 20, Extending Rax). An example of this, is the graphical library, described in Chapter 15, The graphical library, which is a standard part of a Rax distribution. Some of the graphical procedures are implemented externally in C, while others are implemented in Rax. The name of an output procedure must begin with the backtick: `. The definition of an output procedure, has to have the following format:

    ` <- input_type: `procedure_name <-
    {
      // Procedure body here
    };
       

The following example comes from the implementation of the graphical engine:

    ` <- {[&:x, &:y]}: `polyline <-
    {
      `newpath;
      `moveto in(1);
      `lineto in(2..);
      `stroke;
    };
       

In this example, a higher level output procedure, `polyline, is constructed from the number of more basic procedures, such as `line or `moveto.