Mapped operators

SPL takes inspiration from Matlab and supports auto-vectorization or mapping of expression operators to work over lists and maps.

There are two kinds of mapped operators: non-dotted and dotted. Non-dotted binary operators such as *, +=, or - are mapped if one operand is a scalar and the other a list or map. Here are some examples:

void test() {
  mutable list<int32> ls = [1, 2, 3];
  ls = 2 * ls;   // 2                 * [1, 2, 3]      == [2, 4, 6]
  ls += 2;       // [2, 4, 6]         + 2              == [4, 6, 8]
  ls = ls - 1;   // [4, 6, 8]         - 1              == [3, 5, 7]
  mutable map<rstring, int32> mp = {"x":1, "y":2};
  mp = 2 * mp;   // 2                 * {"x":1, "y":2} == {"x":2, "y":4}
  mp += 2;       // {"x":2, "y":4}    + 2              == {"x":4, "y":6}
  mp = mp - 1;   // {"x":4, "y":6}    - 1              == {"x":3, "y":5}
}

SPL also supports dotted mapped operators such as .+ or .*. If both operands are equal-length lists or maps with the same key set, the dotted operators work on corresponding pairs of elements at a time. For example:

[3,2] .* [5,4] == [3*5, 2*4] == [15,8]             // multiply two lists
{"x":4, "y":6} .- {"x":3, "y":1} == {"x":1, "y":5} // subtract two maps

If the operands are lists of different sizes or maps with different key sets, the mapped operator triggers a runtime error. Dotted operators have the same precedence as their non-dotted counterparts.

Table 1. List of mapped operators
Dotted operators Description
.*  ./  .% Mapped multiplication, division, remainder
.+  .- Mapped addition, subtraction
.<<  .>> Mapped bitwise left-shift, right-shift
.<  .<=  .>  .>=  .!=  .== Mapped comparison
.& Mapped and
.^ Mapped xor
.| Mapped or

Mapped operators unwrap only a single dimension. For example, 2 * [[1,2],[3,4]] is not supported, and neither is [[1,2],[3,4]] .* [[5,6],[7,8]], because they both must unwrap multiple dimensions of lists before the operators are applicable.