Squaring the Circle (Diamond?) of TAG! and <>

I cannot accept this behavior, which is in Rebol2, R3-Alpha, and Red:

>> x: first [<>]
== <>

>> type? x
== word!

>> y: clear <abc>
== <>

>> type? y
== tag!

The seemingly simple answer of "just make <> a TAG!" has faced baffling resistance. I've truly never understood the obsession with declaring that <> somehow has to be "not equals". To me, the "has to be" is empty tag. Not only are empty tags useful to represent in source (empty-tag: copy <>), even those willing to give that up they will appear at runtime...as the above sample clearly shows.

But a mechanism pioneered to resolve how / can be a PATH! and yet have a binding like a WORD! can be rewired to apply to <> as well. String cells have room for a binding they're not using. If you bind all strings of any length to some alias--like -tag-0-, for instance--it can carry that binding wherever it goes. If the tag ever becomes empty and is composed into code, then evaluatively it will run.

It would be mostly indistinguishable from a WORD! for most purposes, but would report itself as a TAG!. This would also allow empty file % to act as modulus, or similar. Empty ISSUE! could be # and you could define that too.

It means that to use the empty value as a value, you have to quote it...which is relatively painless, and people would presumably get used to it, or do make tag! N as one historically would.

 >> t: copy <>
 ** Error, <> is missing its right hand argument

 >> t: copy '<>
 == <>

 >> type of t
 == @[tag]  ; in the proposed datatype-replacement scheme

Having to quote it is weird, but, that's the way this particular cookie bounces.

I can think of some ways to make it less likely to get bit by having an empty string come to life by stripping bindings on mutation in certain cells. But you can always manually unbind it. Rather than give a "word is unbound" error, it will just decay to inert behavior.

There aren't going to be SET-TAG!s, so you'll have to write -tag-0-: enfix func [...] [...] or similar to re-assign an instance to bind to. But maybe this could have a trick like:

<>/: enfix func [...] [...]   ; assign something guaranteed to be a function
<>.: "guaranteed not a function"
set '<> :arbitrary-value  ; maybe function, maybe not

Anyway, as with /, the average user won't be able to tell the difference. It's a compromise, but an important one to get us past a years-long impasse, with a strong set of justifications and fitting into the modern implementation.

Were it just me, I'd have used >< for not equals and avoid all this. But now that the mechanics are all here for other reasons, leveraging it here feels clever. People who don't want their tags to be executable can ask their modules to disable it (by defining the -tag-0- as null) and then they can do things like tag: copy <> without the quote.

With This In Place, We Can Commit to the Tag Design

I'm ready to lay down the law on this, and it is very liberal in opening up TAG! representations

Everything that starts with < and ends in > is a TAG!, even with no spaces. That's a finalized decision I can live with and not lament missing <...> "operators", but embrace finding new and innovative ways to apply them as inert signaling labels.

We give up <!-- and --!> from WORD! space, but it's no big loss...and we keep <-- and -->.

The fate of things like abc>def or abc<def (which @Mark-hi has advocated for in the past) is unknown, so that might be a last thinking point.

I remember your suggestion to write <> as =/= when not equal is meant. That would be an option for me too. Even if I already know my code will fail on this issue a lot.

Say to use >< for not equal is even worse than <> as it is even more common to appear in HTML and alike sourcecode than the empty tag is. Nobody else uses "><" (as does nobody use "=/=" but I like that one better if I had to make a choice) and besides that it is ugly looking, and resembling pinched eyes.

In other languages, like C that use !=, do they allow <> also?

Anyway, I get the point and I think your solution will solve this nicely.

Interestingly enough, that almost works...except paths can't dispatch enfix

; add an = refinement to EQUAL? that makes it negate output
; (inherit everything else about equal in the spec + help info)
=: enfix enclose augment 'equal? [/= "negate output"] func [f [frame!]] [
     either f/= [not do f] [do f]

>> 1 = 1
== #[true]

>> 1 =/= 1
** Error: Use `<-` to shove left enfix operands into PATH!s

D'oh. But there are good reasons for this. The augmentation worked though, which is pretty awesome:

>> 1 <- =/= 1
== #[false]

That's power, right there.

=|= is a decent alternative.

and alias that with =\= :slight_smile:

No, I didn't. Ever. I advocated for something called "arrow-words", which do not and can not and never should have letters.

Please reread lexer.reb (GitHub), noting in particular:

Maybe I'm misremembering... or you just drew my attention to some language that existed that allowed it, without actually advocating it.

(Note: GitHub allows you to freeze the version you are at in the URL to the currently viewed commit by pressing Y, and you can get lines or ranges in the link by clicking and ctrl-clicking the line numbers. I updated this to make the links easier)

Yep, reviewed. Your "arrows" definition predates what I've said and matches it. So that aspect is now officially your solution...that I perhaps got by osmosis. But to the extent I wound up at the same conclusion in my own head-space independently, that is doubly good because it makes it more likely to have some emergent rightness.

My unique contribution here is thus just the mechanical trick for <> being a tag and executable, and hopefully that will pan out peachily and this can get tied up.

There are going to be a lot of interesting usages of arrow words. I'd been using => for lambda, but that gets us into debates about whether <= should be for some leftward pointing arrow or greater-or-equal. If we make lambda instead -> then => can just be a synonym for >= in the default configuration, and you can do with it what you like in your own dialects and time.

So having -> be lambda is lighter-weight and is in keeping with better company as matching Haskell and co, instead with JavaScript. Overrideable by anyone who disagrees.

That pushes the shove operators for making a PATH! (or any function) act as infix off to being something else. 10 >> lib/+ 20 and 10 << lib/+ 20 are one pairing of options, but I feel like >> should have a bigger and "heavier" purpose. >- and -< are still light-looking, so maybe 10 >- lib/+ 20 ? Or perhaps 10 >| lib/+ 20 so the whole family of operations is >|, |<, |>, and |<. I'll have to work through what exactly all these mean again (they're in the tests, thankfully, so I can look at all the edge cases that motivate why there are 4 and what they do).

Feeling more confident and stable about all of this, so that's good.

Good, indeed. And thanks for the edits/pointers.
There is only one thing left that needs a mention, and that's </>. Hopefully it will not screw up your pathing stuff. I had to put a special exception in lexer.reb to prioritise making it a tag. It should never be a word, of course.

That needs to be a PATH!, otherwise you couldn't do this:

<: function [/>] [
    ; ... whatever you wanted this to do ...

The legality of < and > as WORD!s means the legality of </ and /> as PATH!s, and so is </>.

If that's the one thing left, it's solved.

That's not going to make everybody happy. I wanted it to be a path too. I think the most vocal advocate was @gchiu, but he certainly wasn't alone even if I'm wrong.

And remember, </abc> and its friends should be tags, they are the closing tags of HTML. They cannot be paths, so there has to be a way of making that / not indicate a path, so maybe that way could be used for </> as well?

In Red:

set '< function [/>] [
    either > [print "refined"] [print "unrefined"]

red>> do reduce [to path! [< >]]

So mechanically it works, but is an inconsistent mess getting there because the scanner is underthought. <: isn't a legal word for them, and </> scans as a TAG!...even though that tag aliases what they run as a legitimate PATH!. (But maybe their new lexer branch changes this, I dunno. Feel free to look into that.)

R3-Alpha is worse:

set '< function reduce [to refinement! '<] [
    either > [print "refined"] [print "unrefined"]

r3-alpha>> do reduce [to path! [< >]]
** Script error: < has no refinement called >

But now we will have the real deal:

<: function reduce [/<] [
    either > [print "refined"] [print "unrefined"]

>> </>

Feels nice to have this stuff becoming solid and high class instead of ghetto. :face_with_monocle:

Just gotta write it.

That way is that abc> is not a WORD!

So only </abc> is a TAG!, </abc > is a PATH! followed by a WORD!.

But @rgchris will be pleased this all pans out such that <abc > can be a tag, and multiline tags are legal...just not multiline closing tags. I kind of wash my hands with it, and as long as whatever rules he wants don't interfere with the mechanics, that's fine.

All this hinged on getting all the ducks in a row for understanding what is and what isn't...and that comes from having a fully coherent definition of PATH!.

I've gone ahead and put something in. I was aiming for getting the right behavior without a tremendous concern for performance (the whole thing should be specified more declaratively, as we all know.)

But for whatever it's worth, here's the change. Note that writing the arrow word handling all in one big first step allowed a bunch of little weird patches down beneath to be deleted.

I'm not sure how many new arrows are going to be getting used. But an immediate application is just being able to get rid of all the janky workarounds that have existed in some form or another since R3-Alpha, to do the current set of useful arrows. This corrects their behavior with quoting, SET-WORD!, GET-WORD!, etc.

Having delved into making tag use the same basic trick as PATH! is using, I don't think it's a good idea. It's technically possible to have a binding in a tag, even if it's not at the head...but the logistics are complicated.

It's not really the same thing as with PATH!, because most paths are executable...so paths as a whole are executable. Starting with a blank suppresses evaluation, but ending in a blank (e.g. a slash) guarantees it...so we just resolve that by saying the latter is the winning condition.

Since all tags are mechanically permitted, the invalid forms will use string rendering with { }. So empty tags are just going to have to be <{}>.