On Tuesday, January 21, 2003, at 03:52  PM, Dave Whipp wrote:
But in a for loop:

 for 1,2,3,4 { ... }
 for 1,2,3,4 -> ($a,$b) {...}

its cuteness works because the brain sees it as a piping operator (even
though its not).
That's an excellent observation. I like the 'for' syntax quite a bit, but never quite could put my finger on why it worked so well. You're right, that's exactly it.

And this begs the question: what exactly does the C<for> contribute to
the semantics of the pipeline? What would C<map> (in void context)
do differently?
Hmm. Perhaps there doesn't _need_ to be a difference. You currently use C<for> when you don't care about capturing the results. You use C<map> when you do. But since we can identify context, perhaps they could be synonyms, if we tweak things a little.

*** SPECULATION AHEAD ***

Note that C<for> allows you to name the arguments, using the pointy sub rule:

for @a -> ($a,$b) {...}

but note ALSO that it'd be great if C<map> and friends had that capability, so you could C<map> two-at-a-time, etc. (C<grep> and C<sort> don't really need it, but I *suppose* you could use it for grepping/sorting tuples.)

So we want the pointy-sub rule for C<map> and friends too:

my @a = map -> ($a,$b) {...}; # pass to {...} 2-at-a-time, as $a and $b

Pretty neat.

But ignoring pointy-sub for the moment, and assuming that we in fact used -> and <- to mean pre/post-invocant (e.g. "pipes"): if we defined C<for> as just another word for C<map>, what we really want is rules such that *all* of the following work. Question is, is it possible to come up with something consistent. Maybe.

# void context

@a.map {...}
@a.for {...} # identical
@a -> map {...} # identical
@a -> for {...} # identical

@a -> {...} # identical (a cool shorthand when given a closure?)

for @a {...} # Doh! This wouldn't work
for @a : {...} # Hey, but this would (indirect object syntax)
map @a : {...} # identical

for @a,@b,@c : {...} # is this OK?
for (@a,@b,@c) : {...} # OK, and better looking

# list context

my @a = @b -> for {...}
my @a = @b -> map {...} # identical
my @a = map {...} <- @b # identical
my @a = for {...} <- @b # identical

my @a <- map {...} <- @b # identical
my @a <- for {...} <- @b # identical

That works. Pretty darn well, too. (Note the critical bit, the introduction of indirect object syntax to C<for>.)

*Now*, what to do about the fantastic magic that pointy-sub provides? The _spectacular_ win would be if we could just recognize an optional parameter list as part of a block.

map @a : ($a,$b) {...} # params + closure = closure with params?
for @a : ($a,$b) {...}

So that anywhere you had a closure, you could put a paramlist in front of it. Not likely we could get that to work, since best-case scenario it would probably barf on $a and $b being undefined before it ever got to the closure. But it might be possible, at the expense of some parser complexity.

If we couldn't do that, you would have to say:

for @a : sub ($a,$b) {...} # OK
or
@a -> for sub ($a,$b) {...} # a bit icky

The first of those looks better than the second. I loathed the second after I first wrote it, but now it's beginning to grow on me a bit better. The intent is certainly clear, for example.

The pointy-sub syntax doesn't really work in these cases, though. I can't think of anything you could use in place of 'sub' that would make either of those look better.

So, the benefits of trying to pursue this: It makes C<for> and C<map> synonyms, and gives each the magical powers of the other. The fact even C<for> is now an object method means you could say:

for @obj : {...}

and even overload the C<for> behavior for a Collection-based @obj. It also gets rid of the perl5 bugaboo:

for (@a) {...}
map {...} @a; # params are reversed. Newbie trap!

So the biggest single trick is, C<for> would have the colon after the list:

for @a : {...}

If we did that, the rest might just work.

Anyway, just speculation.

MikeL

Reply via email to