Boris's "NEW-EACH" Loop Dialecting Proposals

Here are some ideas for what kinds of things you might put in a FOR-EACH variable block besides just words, and some other ideas:

foreach-design.md · master · hi i am boris / Red mezz warehouse · GitLab

It's definitely worth thinking about how to ask for the iteration counter automatically while iterating. Or getting the position and the value somehow at once, instead of being forced to iterate by position and remember to say pos.1 to get the value. We've had some hare-brained schemes for that come down the pipe.

The simple idea of just asking for it in the vars spec feels pretty solid. I'm not thrilled about the /var notation, but it's a desirable feature and certainly in reach:

>> for-each [/i x y] [#a <b> #c <d> #e] [
       print [i x y]
   ]
1 #a <b>
2 #c <d>
3 #e

But some of the other proposals are a bit heavy, like a type constraint and then skipping out of iterations where the type doesn't match:

>> for-each [/i x [text! group!] y] ["a" <b> #c <d> (e)] [
       print [i x y]
   ]
1 "a" <b>
3 (e)

The semantic combinatorics start to add up. He says that the index keeps counting regardless of whether the iteration was skipped or not... but, is that obvious?

And isn't the following kind of clearer:

>> for-each [/i x y] ["a" <b> #c <d> "e"] [
       match [text! group!] x else [continue]   
       print [index x y]
   ]
1 "a" <b>
3 (e)

Things to Avoid: Overdesign, "Parts of Speech" Abuse

I think we should be cautious about overdesigning such core constructs. (But certainly these things--whether you think they are monstrosities or not--are exactly the kind of thing that people are supposed to be empowered to make if it pleases them. It's supposed to be a personal language, and you personalize it to the extent of your tastes.)

As we want to know the loop iteration you're on, maybe the better angle isn't to be abusing word decorations in such weird ways...but to use refinements as labels and then name the variable after it. I wouldn't call the loop counter the "index" because the index is in the series position... so, /COUNTER or /COUNT maybe?

>> for-each [/count i x y] [#a <b> #c <d> #e] [
       print [i x y]
   ]

Commas would probably make it clearer, so good we have them:

>> for-each [/count i, x y] [#a <b> #c <d> #e] [
       print [i x y]
   ]

You could get the series position with /here. And @BlackATTR and @gchiu and I have talked about things like wanting to know if you were on the first or last iteration. Well, why not /first? and /last?

>> for-each [/first? f, /last? l, x y] [#a <b> #c <d> #e] [
       print [f l x y]
   ]
true false #a <b>
false false #c <d>
false true #e

Some of these could apply to other loop forms like COUNT-UP I guess...

>> count-up [/first? f, /last? l, x] 3 [print [f l x]]
true false 1
false false 2
false true 3

People who really want shorthands to say that "just a refinement means /count" could put a little preprocessor adapting the spec to the more verbose form. But I'd rather use the verbose version.

Generalized Generators Throw A Thorn in This

In moving to a generalized FOR that runs over a generator, the values are being provided by something that gets called N times and returns answers. The underlying pattern can be shown by the rewrite:

>> for [/first? f, /last? l, x y] (each [#a <b> #c <d> #e]) [
       print [f l x y]
   ]

EACH is a generator. And FOR is something that calls that generator N times.

But not all generators operate on series, some fabricate data out of thin air. So they'd have no series position to answer /HERE with. And while we can universally answer the "is this the first iteration" in the iteration construct itself, not all generators know if they're on the last time you're calling them...they provide data for each call until they return NULL.

So I guess the mechanics would have to be to try and request these as output parameters from the generator, and it either has them or it doesn't. Yay for multi-returns, amiright? :slight_smile:

But since the answers are coming from the generator you'd probably have to put your questions for it in front of the variable for the iteration # you are asking:

>> for [/first? f, x, /last? l, y] (each [#a <b> #c <d> #e]) [
       print [f l x y]
   ]

This moves the LAST? question so it would ostensibly be grouped with the request for Y.

Weird Thought... "FOR specs", like "FUNC specs"?

It occurred to me like we might actually want to think about documenting these like specs.

>> data: [#a <b> #c <d> #e]

>> for-each [
       /first? f [logic!]
       x [tag!] "Maybe document what this is for with a TEXT!?"
       /last? l [logic!]
       y [issue!] "Same here..."
   ] data [
       print [f l x y]
   ]

It could introduce concepts like <skip> able parameters.

Of course you wouldn't have to use such wacky things if you didn't want to, I'm just saying you could. :slight_smile:

3 Likes