Much Ado About A Tiny Email Test "Micro-Dialect"

I've been pondering this question of using BLOCK!s for type checks.

We could imagine a world in which FENCE! is used instead of BLOCK! for things like the FOR-EACH variable list.

for-each {:bad [tag!] text [text!]} [
        -[email@example.com]-
        -[firstname.lastname@example.com]-
  <bad> -[email@example@example.com]-
        -[email@subdomain.example.com]-
        ...
][
    ...
]

And we could also imagine a world in which function specs used FENCE! to indicate typesets, and this was carried over to things like FOR-EACH

adder: func [x {integer!} y {integer!}] [
    return x + y
]

for-each [:bad {tag!} text {text!}] [
        -[email@example.com]-
        -[firstname.lastname@example.com]-
  <bad> -[email@example@example.com]-
        -[email@subdomain.example.com]-
        ...
][
    ...
]

I think this would likely be misguided uses of FENCE!. While there are some questions raised by using FENCE! for WRAP that I haven't fully resolved for myself, it's still very high-leverage.

Or we could say destructuring is analogous to splices, hence if you destructure in a FOR-EACH you use a lifted splice notation:

for-each ~[:bad [tag!] text [text!]]~ [
        -[email@example.com]-
        -[firstname.lastname@example.com]-
  <bad> -[email@example@example.com]-
        -[email@subdomain.example.com]-
        ...
][
    ...
]

Furthermore, destructuring could vary based on whether you're taking sequential iterator values or unpacking single iterator values (I've been thinking the OBJECT! iterator gives back PACK!s of the key with the value):

obj: make object! [a: 10 b: 20 c: 30 d: 40]

for-each ~[key1 key2]~ obj [probe key1 probe key2] 
=> a b c d

for-each ~(key val)~ obj [probe key probe val]
=> a 10 b 20 c 30 d 40

You could do hybridizations at that point:

for-each ~[key1 ~(key2 val2)~]~ obj [probe key1 probe key2] 
=> a b 20 c d 40

Note there's a subtle difference between for-each ~[^x]~ and for-each ~(^x)~ in this model; as one of them is unpacking and the other is not. So the first one would give you a PACK! if the iterator made a pack, and the second one would give you the unpacked first item in a pack-potentially itself unstable.

(for-each ~[x]~ and for-each ~(x)~ could be defined as being identical if desired; e.g. if the thing wasn't a PACK! it would just be assigned normally... this would be consistent with [x]: 10 being legal even though 10 is not a PACK!... today that is allowed though I'm not sure if it should be allowed...)

I'd thus assume a conventional for-each <whatever> would be interpreted as for-each ~[<whatever>]~

We could also say that since you're unlikely to say for-each [integer!] data [...] in which you validate each item but don't have a name for it, we presume a plain BLOCK! is synonymous with a quasi-block.

...OR we could say since you didn't give the variable a name, we just automatically name the variable something like ?

>> for-each [integer!] [1 2 "foo"] [print ["Integer is" ?]]
Integer is 1
Integer is 2
** PANIC: FOR-EACH expected [integer!] in data, got "foo"

Enabling Dialecting Feels "Worth The Tildes"

So much of Rebol's promise lies in showing off this kind of power.

But to get there, you have to be able to leverage composition.

Achieving composition means being able to use the same forms in different contexts. And making the FOR-EACH specs look like function specs is a pretty powerful way of doing that.

If you think about it, being able to iterate and capture multiple variables at a time is a kind of "strange" feature, so having a notation that helps annotate that you're "unsplicing" or "unpacking" and saying which you are doing isn't that crazy an ask.

And once you have people telling you which they're doing, and allow it to nest recursively, especially with the ability to say when certain fields are optional, you get some neat features...

2 Likes