So now that the NULL isotope concept exists, it is possible to assign one of them to mean a generator is "FINISHED" (plain NULL) and another to mean "I really meant return NULL, and I may have more values to emit." When you're doing a "map-each" the intentional return of NULL is particularly important, for saying you want to add nothing to the collection but keep going.
The third state is BREAK--which is currently handled by another mechanism (THROW). There are two possibilities for who's in charge of catching the throw:
- iteration constructs like FOR or MAP could catch the BREAK, and return (plain) NULL
- generators like EACH could catch the BREAK, and then use some third signal (NULL-BREAK?) to communicate the intent to FOR or MAP as distinct from "finished" or "actually returned NULL".
It seems kind of obvious that (1) is preferable, but I'm just mentioning (2) for completeness.
So it looks like we can try this to see how it goes. Though a single-arity MAP function is not that typical. The norm is two inputs: a collection of things of category A, and a function that transforms As into Bs. Then you get a collection of B's out.
Getting the conventional arity-2 MAP out of this to use with a function becomes messy, because Rebol has historically had this "put your iteration variable's name in a certain position" situation, and if you don't need the variable (because it's implicitly a function argument) that position has to have a placeholder
>> map each _ [1 2 3] :negate
== [-1 -2 -3]
This could be addressed with a lambda syntax:
>> map each [1 2 3] x -> [x + 1]
== [2 3 4]
So then EACH is arity 2, and MAP is arity 1. But I think that this would be one of those cases of drifting away from the Rebol spirit and starting to systemize the parts too much to where you're back to "being beholden to pure functional programming syntax limits...but without any of the pure functional programming benefits."
...or we say that you use different constructs:
>> map each x [1 2 3] [x + 1]
== [2 3 4]
>> map each* [1 2 3] :negate
== [-1 -2 -3]
But I find that grating, so the idea that keeps recurring to me is to use a skippable QUOTED! parameter:
>> map each 'x [1 2 3] [x + 1]
== [2 3 4]
>> map each [1 2 3] :negate
== [-1 -2 -3]
This has the downside (upside?) that you always have to distinuish a named loop variable by quoting it. That applies to BLOCK!s of variables too...to distinguish a block of variable names from a block of data to enumerate:
>> map each '[a b] [1 2 3 4] [reduce [a + 1, b + 2]]
== [2 4 4 6]
(We might shorthand this as map each 'a.b, a concept being thought about for lambdas too.)
I'll send everyone back to the not-commented-on-but-very-important "Speaking with tics" regarding this contentious (at least in my mind) matter.
Perhaps when we see this all laid out where we can get rid of the hyphen in FOR-EACH and MAP-EACH, the total cost of needing the apostrophe on the variable levels out. It also can't be denied that it helps communicate that it's a "name" and not a "use", and means that if the word is incidentally a left-quoting ENFIX operator it won't be "live" and try to quote the EACH.
This doesn't necessarily have any bearing on type of foo vs. 'type of foo. While the issues are related, different decisions can be made on each case according to what is best suited.
Broader Impliciations
If something like this is pursued, the question comes up as to how many constructs should use this "generator" style.
loop 10 [print "Hi"] is pretty straightforward. You wouldn't want LOOP to generate a function, and then have to write for loop 10 [print "Hi"].
So this needs to be weighed in consideration--though maybe it suggests things like LOOP do use generators, but are macros which subsume the FOR into their implementation.