TUPLE! and PATH! Big Picture Concept

In the past I've suggested the idea that TUPLE! run on the basic same schematic as paths.

a.b.c  ; a TUPLE!
a/b/c  ; a PATH!

While decimal numbers like 10.20 have proven controversial to switch to be TUPLE! (e.g. by pushing DECIMAL! to use the 10,20 international format), they should be able to hold everything else PATH!s can today (which includes BLOCK! and GROUP!).

PATH!s have also become more interesting in terms of ways to augment a single WORD!...subsuming REFINEMENT! and also allowing forms that have a BLANK! in the second spot, rendering with a terminal slash:

/a   ; now a PATH! (with a BLANK! in the first spot)
a/   ; also a PATH! (with BLANK! in the second spot)
/a/  ; this works too (3-element PATH! with blanks in first and last spots)

Then of course, these would apply to TUPLE!s as well, giving you fun new tuple variations:

.a
a.
.a.

This gave me an interesting idea about how to define the behaviors, which could also solve longstanding concerns in the system:

What if TUPLE!s evaluated exactly as today's GET-PATH!s would, never executing functions?

This would let you bulletproof your code a bit better when picking things out of objects that you expect to be values. You'd use dots. Terminal dots could be useful too...as a slightly more subtle way to ask a WORD! not to execute than to use a GET-WORD!.

append block data.

You could read that out as "append block, data...PERIOD." Do not call data, it is not a function. This would mean :my-object/field/subfield could look much cleaner as my-object.field.subfield (no terminal dot needed since the last internal split is a dot).

I'd suggest then that ending with a slash means that you guarantee the thing you are calling is a function.

append block data/

If you don't want to specify you leave it off, as today.

My proposal would be that only terminal slashes act this way...for compatibility. It would be your choice to use a slash internally to a path for polymorphism (as there is no other polymorphic option).

With this, you could then write:

obj.member-obj.method/refinement whatever

If you were in the practice of only using / when calling functions then this would unambiguously suggest to you that method must be a function. Again--this would not be enforced.

I think this would really help decipher expressions like a/b/c/d/e/f if they were written as a.b.c/d/e/f or a.b/c/d/e/f or a.b.c.d.e/f (or a.b.c.d.e.f/ or a.b.c.d.e.f. if you wanted to be more explicit about whether the end of the chain results in a function call or value).

There are some bigger implications here about having another way of indicating suppression or execution with the terminals. I've often worried about the ambiguity of whether GET-WORD! meant suppressing execution or tolerating undefined values...which we had made an interim resolution to say that it only suppressed execution, and GET/ANY was necessary to get voids (as in Rebol2). But this throws in new possibilities so that should probably be rethought.

1 Like

Note that although this proposal would take away "." from WORD! characters, it would not remove "." from being able to reach values. So if you had charsets.latin1 it could still find it, but you'd just have to have CHARSETS defined as an object.

The twist would be that if you wanted to call a function with . in the name, you'd have to stick on a terminal slash (or use REEVALUATE (abbreviated REEVAL), or APPLY, etc).

some.fun/ arg1 arg2
reeval some.fun arg1 arg2

But I do think it builds on solid logic to the idea that period isn't a word character (while it makes sense for apostrophe to be). If you use periods it really seems like you're indicating some kind of structural intent...as opposed to hyphens or underscores, that are more typically "parts of the word".

(Note: I'm very tempted to think that this could all be used somehow to suppress assignment of functions, e.g. foo: .bar or foo: bar. will guarantee bar is not a function, or something like that. But it needs to be thought through consistently. This gets at some of the bulletproofing that I think is worth it for libraries, but maybe not the average user code... )

1 Like

:clap: I think this is clever, I'm curious to see what kind of reception this gets.

1 Like

So the rule for the TUPLE! would be "things to the left of a dot can't be functions".

append block some.item   ; SOME can't be a function, but ITEM can...not executed
append block some.item.  ; neither SOME nor ITEM can be a function

I'm not sure what the implication would be for a.b/c.d when b is a function. :-/ This would mean C and D would be refinements or something? There's no need for it to have meaning...it could just be an error (and saved for dialects that wanted to make use of it).

What's really neat is that the trick for using single cells for PATH!s that contain only one item has now been deployed for the / trick that @giuliolunati wanted. It's creaky at the moment, but the concept is proven. That means we can get all of [.a a. .a. /a a/ /a/] each for the same price as a single WORD!.

1 Like

This means decimal numbers in paths/tuples will require forming with comma ",". It is somewhat similar to the date case, in that date slash-forms cannot appear in paths/tuples, however in the decimal number case the form with comma is not the default form. So, decimal numbers will form differently whether they are in a path/tuple or not (unless you want to change the default form to use comma, which I am against).

In other "random thoughts", I think that PAIR! and TUPLE! can be unified...such that a 2 element numeric-oriented tuple renders with an x. Then PAIR! would be a "type constraint class" (like how REFINEMENT! will be a constraint on PATH!s of length 2 with blank at the head).

This would save on a lot of redundant elementwise code...recover one of the 64 internal type bytes and bring PAIR's 2-element-"pairing" series optimization) where 2 REBVAL fit in a REBSER node with no external allocation) to PATH! and TUPLE!.

Type constraints are effectively predicates, so maybe they would be done e.g. as .refinement?

 >> any .refinement? [/a/ b/c /d ///e]
 == /d

 >> print-pair: func [pair [.pair?]] [print mold pair]

 >> print-pair 'a/b
 ** Error: print-pair expects its pair argument to match PAIR? constraint

I dunno. Also, there's a question of whether 1/2 should be legal as a PATH!. It currently conflicts with DATE!, but should probably stick to 12-Dec-2012 forms...because slashed numbers seem more valuable to have...and it makes up for not having 1x2 instead of 1.2