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

So there was a "micro dialect" for email address scanning. A simple idea that you could intermix valid and invalid email addresses...to put groups of related emails together even though some in the group would fail and others would succeed.

It was something along these lines:

for-each [mode text] [
    + {email@example.com}
    + {firstname.lastname@example.com}
    - {email@example@example.com}
    + {email@subdomain.example.com}
    + {firstname+lastname@example.com}
    - {email@example.com}
    + {email@123.123.123.123}
    - {email@[123.123.123.123]}
    ...
 ][
    assert [mode: select [+ valid - invalid] mode]
    if (mode = 'valid) <> test-scan-email (...) [
        panic ["Expected" @text "to be" an mode "email"]
    ]
 ]

But for this post, I threw in a couple of "hey that's neat" aspects, like:

>> an "valid"
== "a valid"

>> an "invalid"
== "an invalid"

Enter Dashed Strings

I don't know that the + and - markers were ever the greatest, but they certainly lost their appeal with dashed strings.

    + -[email@example.com]-
    + -[firstname.lastname@example.com]-
    - -[email@example@example.com]-
    + -[email@subdomain.example.com]-

At first I figured I'd just pick an alternative. There's Y and N...

    Y -[email@example.com]-
    Y -[firstname.lastname@example.com]-
    N -[email@example@example.com]-
    Y -[email@subdomain.example.com]-

Those are pretty big letterforms that blur together some. Tilde (a.k.a. "Quasar", quasi-BLANK!, lifted VOID!) carries the connotation of "something wrong/missing"...

    Y -[email@example.com]-
    Y -[firstname.lastname@example.com]-
    ~ -[email@example@example.com]-
    Y -[email@subdomain.example.com]-

...but it blurs here too much with the dash.

Really we can ask: why are we decorating the valid things, and not just the invalid things?

      -[email@example.com]-
      -[firstname.lastname@example.com]-
    # -[email@example@example.com]-
      -[email@subdomain.example.com]-

You could use N or * or # or any other nasty here, and it sort of stands out. Not as well as <bad> would.

      -[email@example.com]-
      -[firstname.lastname@example.com]-
<bad> -[email@example@example.com]-
      -[email@subdomain.example.com]-

Or maybe <!!!> or <!>... cuter and doesn't visually interfere as much with the "texty" content, though not as "literate":

    -[email@example.com]-
    -[firstname.lastname@example.com]-
<!> -[email@example@example.com]-
    -[email@subdomain.example.com]-

But if you're going to break the regularity of the structure, you can't use (today's) FOR-EACH.

If you want to regularize it a bit, you could use something like FENCE! to mark the bad ones:

      -[email@example.com}-
      -[firstname.lastname@example.com]-
     {-[email@example@example.com]-}
      -[email@subdomain.example.com]-

If that didn't stand out enough, you could use a double-fence:

      -[email@example.com]-
      -[firstname.lastname@example.com]-
    {{-[email@example@example.com]-}}
      -[email@subdomain.example.com]-

It's worth remembering such things are options in some cases, but I don't think that works very well here.

<bad> Seems Good :grin: But Could It Be Easier?

It would be nice if there were some way to type the FOR-EACH variables, and denote their optionality.

@hiiamboris has done some things in this vein, see his type filter on FOR-EACH proposal. I feel like the concept of skipping and checking should be separate intents. I can want to type check something but not want to skip it.

A leading colon could imply optionality, as it does with refinements now:

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

You can even use <bad> itself for the type check by putting it in a quasi-splice, and enforce/document that more stringently. Also, for the sake of "how does that look in the generator model" I'll write it out that way:

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

Of course you can split this out to a table vs. having the tests inline like that.

Interpreting blocks as type checks seems pretty useful, but that takes away from some other applications which might be used for destructuring.

Anyway, this is just some thinking inspired by a very small example.

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