Austin Hastings writes:
> Suppose I want to say:
> 
>   sub sublist(@a, $start, $cnt) {
>     return @a[$start] +next --$cnt;
>   }
> 
> where +next is a binary macro that takes as its lhs an array element
> access, and its rhs a number, and returns a list of the array element
> followed by the next RHS elements.

First, I don't believe you can do that, as the macro name comes after
one of the things it needs to specially parse.  So first, let's turn it
into:

    sub sublist(@a, $start, $cnt) {
        return with_next(@a[$start], $cnt-1)
    }

(You can't modify $cnt unless it's declared C<rw> or C<copy>.  Does -1
achieve the same semantic effect you were looking for?)


> It seems obvious to me that +next has to be a macro, since it has to
> detect this weird condition of "accessing an array element". How do I
> code that?

True, it does.  You could do that with a custom parse (requiring a copy
of the Perl 6 grammar in order to know where to hook in).  But, judging
from below, I think you're getting at something else.

> More generally, is there any way (via trait or property or whatever)
> to recognize when something as basic as $_ is an array element, and
> get the containing array(s) and somehow do the right thing?
> 
> (I can't count the number of times I've been frustrated by map/grep
> because there's no way to get at the "next" or "prior" element in the
> sequence.)

I, too, have been frustrated by this a couple times.  More commonly it's
when I'm C<foreach>ing over an array, and I have to switch to a C-style
for because I need to get the next element.

It's not possible to recognize when something like $_ is an array
element, as it might be an element of more than one array.

The best solution I can see right now is to make the parameter(s) to
for, map, grep, etc. an iterator instead of just the value.  In fact, I
talked about this in a thread entitled "The Object Sigil" awhile back.

The idea was to make $_ delegate in all cases, and have a specific
operator that marks when I<not> to delegate.  This is because it feels
wrong to me to delegate in most cases except for certain methods.  Like
junctions do.  Ick.

But we've already been through that, so I won't bring it up again.

You might have:

    for @foo {
        print if .next < $_;
    }

To print the local maxima of a sequence.

The problem comes up when the objects you're iterating over also have a
C<next> method.  I think of something like a C<real> method, that lets
you access the original, non-iterator object:

    for @foo {
        print .real.next;      # $_'s next, not the iterator's next
        print .real.real.next; # It's possible that @foo contains iterators
    }

That seems like a pretty good solution.  The only problem is that the
user of the iterator needs to know all the methods that an iterator has
in order to stay out of trouble.  Makes it pretty hard to add methods
without breaking things.

Turning that upside-down we get a slightly uglier, but maintainably much
nicer solution.

    for @foo {
        print .next;           # $_'s next, not the iterator's
        print .iter.next;      # The iterator's next
    }

In fact, this is precisely what "The Object Sigil" was proposing, except
it used punctuation instead of a method.  Either way works.

Then you get the slightly esoteric case when $_ has a 'iter' method
itself.  That should happen so infrequently that this should suffice:

    for @foo {
        print .iter.real.iter;
    }

Bringing it back home, it's possible that an array access C<@foo[$ind]>
would return a similar iterator, making the answer to your question
obvious.  However, at first glance, that seems like a bad idea. I'm a
little too tired to explore it more fully at the moment.

Luke

Reply via email to