Sunday, December 30, 2012

In my last blog post I demonstrated using Gazelle to write a quick Tampermonkey script. That example necessitated an aside about the nature of operators in Gazelle. I'd like to say a bit more about that in this post and introduce a small Gazelle featurette.

Second Class Citizens

Freedom for Operators!


Most languages do something which I consider quite odd: they take a set of operations and, rather than providing them to the user as functions, with all the associated first class benefits thereof, they furnish them as mere operators. In Javascript, these benighted second-class citizens count among themselves:
+ - / * ^ % < > <= >=
And others. These operators support infix syntax, the benefits of which I believe, in accord with Lispers everywhere, are overrated, but this post is not about infix expressions. It is about the peculiar and unnecessary distinction between operators and functions. That is, in Javascript, we are unable to say something as reasonable as:
reduce(+, [1,2,3,4])
Even though nothing about the nature of + dictates that this ought to be verboden. On the contrary, given an array and a + function, this is about the most reasonable thing you could want to do with them. Why this state of affairs persists in modern programming languages I will not presently speculate.

Operators in Gazelle

The unusual status of these functions in Javascript poses a problem for Gazelle, which tries to graft a Lisp-like language onto it. In all Lisps I know of, the operations listed above are exposed to the user as plain functions, so that one can write:
(reduce + (list 1 2 3 4))
For instance. Ultimately, I feel that in day to day programming its indispensable to have basic operations be functions. Parenscript, which is otherwise a great piece of software, lets you write:
(+ 1 2 3 4)
which lulls you into a false sense of security pertaining to the nature of +, but produce code which results in an error if you write:
(apply #'+ (list 1 2 3 4))
Because + is not a function. Gazelle takes a somewhat more conservative approach. Without loading any other code, there is no + operation and you cannot write even (+ 1 2).
What you can do is refer to the underlying Javascript operator using the notation Gazelle uses for Javascript primitives, eg: _+. One can write:
(_+ 1 2)
But cannot write (_+ 1 2 3 4). Why not? Because the idea of the primitive operations is to expose, as closely as possible, the behavior of the underlying Javascript. The Javascript + operator is a binary operator, and so the Gazelle _+ operator is also one.

Getting Operator Functions in Gazelle

Because of the way that Gazelle works, you can write operator functions pretty easily, but it is a bit confusing as to what is going on, so lets go through it. Suppose we want a function denoted by + in Gazelle, which is like a Lisp + function. Here is how we might write it:
(define (+)
     (_if (_=== 0 (.. arguments length))
          ((_throw "Plus requires at least one argument.")))
     (var total [arguments 0])
     (for ((var i 1) (_< i (.. arguments length)) (set! i (_+ i 1)))
          (set! total (_+ total [arguments i])))
     total)
Where this is almost pure Javascript. This renders into the following:

    var plus = function ()  {
      if ((0===(arguments.length)))    {
        throw ("Plus requires at least one argument.");
        };
      var total = arguments[0];
      for (var i = 1;(i<(arguments.length));i = (i+1))    {
        total = (total+(arguments[i]));
        };
      return (total);
      }

Note that at the level of Javascript, the function we just defined is called plus, not +. And note that all the references to _+ in the body have been transcoded to + in Javascript. If we refer to + in Gazelle, we refer to plus in Javascript, and if we refer to _+ in Gazelle, we refer to + in Javascript. Hence, we can go about our business using + in Gazelle and everything works.

A Systematic Solution

In 'idiomatic' Gazelle, which, since I am the world's only Gazelle programmer, means however I program, one is supposed to use modules, and one of the most important modules is the operator-functions module, which defines all the common operators as functions and/or macros. One writes:
(require (("hooves/operator-functions" :all))
  (+ 1 2 3))
To use all of the definitions in the module.
Sometimes, as in the Tampermonkey code, we cannot easily use the module system, because we want to write standalone scripts. In that case you can either get by with simply using the primitive operations or you can now say:
(gazelle:essentials)
at the top of the file, which is a "built-in" macro (defined in proper.el) that expands to definitions of all the common operators. This is a reasonable solution if you need to write a standalone script or otherwise don't like the module system. Feel free to implement your own solutions. That is what Lisp is all about.

No comments: