I was writing a PARSE example up with two COLLECTs in it, and I messed both of them up at first...
-
I expected collect some integer! to work, instead of collect some keep integer!.
-
I wrote collect keep some gather instead of collect some keep gather
So I can see the appeal of a KEEP-less COLLECT. But it's tough to implement cleanly; I don't think putting the COLLECT underneath an iteration and having it sneakily retain memory across those iterations is practical (though clever).
One line of attack in PARSE would be a construct that implies both iteration and collection together, so you could say something like collect-some-keep integer! Though that doesn't give you a way to express a difference of tolerating empty collections, you'd need collect-try-some-keep if you wanted that.
Maybe having a thing and calling it ACCUMULATE would be useful?
>> parse [hello 1 2 3] [let w: word! (print [w]), accumulate integer!]
hello
== [1 2 3]
It could have the at-least-one semantic, and then you could TRY ACCUMULATE and get a NULL if there weren't any (as opposed to an empty block). Then maybe you resort to COLLECT if you always wanted a block. Or vice-versa.
Maybe a variant like ACCUMULATE* could give back NULL if there's no YIELDs, e.g. the function it calls just returns NULL. (there's a COLLECT* that does this if there's no KEEPs.)
>> collect* [keep 1 keep 2]
== [1 2]
>> collect* [print "No keeps!"]
No keeps!
== ~null~ ; anti
A non-combinator ACCUMULATE could be applied to generators, as above:
>> accumulate generator [
for x each [1 2] [yield x * 10] yield [a b] yield spread [d e]
]
== [10 20 [a b] d e]
If you passed ACCUMULATE a BLOCK! it could assume you wanted that block to be a generator:
>> accumulate [
for x each [1 2] [yield x * 10] yield [a b] yield spread [d e]
]
== [10 20 [a b] d e]
Again, that's close to COLLECT and KEEP, minus the ability to KEEP/LINE or KEEP/PART or KEEP/DUP.