S09 states (in "Parallelized parameters and autothreading") that if you
use a parameter as an array subscript in a closure, the closure is a
candidate for autothreading.

    -> $x, $y { @foo[$x;$y] }

Means:

    -> ?$x = @foo.shape[0].range,
       ?$y = @foo.shape[1].range { @foo[$x;$y] }

And each range is automatically iterated.

This is considered by some to be far too subtle.  A simple error in
number of parameters passed could result in very strange semantics;
also, declaring some dimensions for efficiency can wildly change some
semantics. The situation is made somewhat better by the fact that this
only happens on arrays that have predeclared dimensions (though I'd
argue that that's even more subtle).

I have a different idea.

Let's put the current meaning of the qw   aside for the moment.  We'll
now use them as threading brackets.

The bracketing construct  @foo[$^i]  makes a junction-like object
threaded over all reasonable values of $^i.  Similarly, 
 @foo[$^i] * @bar[$^j]  creates a two-dimensional object which is the
outer product of @foo and @bar under multiplication (just iterating over
all values of $^i and $^j).

In the case that the values of $^i and $^j cannot be determined from the
way they are used, some extra syntax will be necessary.  I'm not sure
what that is (suggestions welcome).

In list context, the objects expand out into appropriately-dimensioned
lists.  In scalar context they behave much like junctions, threading all
operations, but they perform inner products as many times as necessary.

So:

    my @result = Â @foo[$^i] + @bar[$^i] Â

Is the same as:

    my @result = Â @foo[$^i] Â + Â @foo[$^i] Â

If you give it a statement without placeholders:

    If it's a plain array, it creates an appropriately dimensioned
    object.

    If it's a scalar and an iterator, then it iterates it.  If it's any
    other scalar there is an error.

These are lexical distinctions (except for checking whether something is
an iterator).

Here comes the fun part.

The typical hyper operation now looks like:

    my @result =  @foo  +  @bar Â;

And we can drop the outer brackets, saying that they're implied in this
simple case.

    my @result = @foo Â+Â @bar;

And we also have a list iterator notation:

    for Â$fh {
        say .uc;
    }

Unfortunately, the scalar iterator notation would have to be different.
But perhaps it should be.

Here are some examples derived from S09:

To write a typical tensor product:

    C_{ijkl} = A_{ij} * B_{kl}

You write either of:

    Â @C[$^i; $^j; $^k; $^l] = @A[$^i; $^j] * @B[$^k; $^l] Â
    @C = Â @A[$^i; $^j] * @B[$^k; $^l] Â

Or to write another typical tensor product:

    a^j = L_i^j b^i

You write either of:

    Â @a[$^j] = @L[$^i; $^j] * @b[$^i] Â
    @a = @L Â*Â @b;

(The last one works because the first index of @L is the one we want to
iterate over---like PDL threading)

As for stealing the french brackets, I think that it's a justified
cause.  They're already used for hyper operations, and this is just
generalizing that.  I argue that the interpolating qwÂÂ meaning will be
the most neglected quote around.  For it to be useful, you have to be
slicing on variables and constants at the same time, which is quite
uncommon.

Luke

Reply via email to