You can has GET-BLOCK! (SET-BLOCK!, GET-GROUP!, SET-GROUP!)


#1

Yep, they’re implemented!!! Go try them out!!!


Years ago, discussions circled around what was so special about WORD! and PATH! that they should be the only ones with LIT-XXX! forms. It seemed this was a bit that could equally apply to anything, e.g. group: '(1 + 2).

It took a while, but eventually a general and clever solution was discovered. There’s no LIT-XXX!, just QUOTED! as a container for any type…with any level of quoting. It’s done with neat tricks!

…but what about SET-XXX and GET-XXX?

In that same vein, we might ask why PATH! and WORD! are the only types with SET-XXX! and GET-XXX! forms. Aren’t those just bits too?

GET-BLOCK! seems very useful if it would do a REDUCE in the evaluator. Instead of return reduce [x y] you could just say return :[x y]. SET-GROUP seems like it might be a little tidier too…

word: 'x
set word 1020

word: 'x
(word): 1020

It’s only one less character. But whether you have a preference for one or the other visually, the second can be done faster; the evaluator doesn’t have to look up SET to find the function, nor go through the trouble of filling function frames and dispatching. It’s much lighter-weight.

Yet it seems a slippery slope…

GET-BLOCK! and SET-GROUP! sure seem useful, but if we look at this as more cases of the “lit bit”, where does it stop?

If every type has a “get bit” and a “set bit”, can you have both of them? How does this mix with the LIT-XXX forms? Is there a difference between a LIT-SET-PATH! and a SET-LIT-PATH?

What would a SET-URL! look like? How are the colons mixing up with literals that have colons in them? (:15 is a TIME! in the current world…, that would become a GET-INTEGER!, so what does a GET-TIME! look like?)

This foiled ideas of the past, like trying to get rid of SET-PATH! and GET-PATH! and just say, e.g. “a SET-PATH! is one with a SET-WORD! in its last slot”. Because then to get x/1: you would need SET-INTEGER!, to get x/(y + z): you’d need SET-GROUP!, and it just seemed to go to chaos.

In the absence of a clear plan, we plodded along with the status quo, awaiting some moment of clarity.

Moment of clarity arrived: Just add BLOCK! and GROUP!

If we only allow SET and GET forms on containers, it’s a clean model (no worries about intersecting with lexical rules of the contained types). And you get your dialected parts. Need a SET-INTEGER! ? Well, you no can has. But with containers, you get it all:

 my-dialect [
     [1]: "It's just as good, as `1:`, really!"

     :(:15) "Even better, as if you want GET-FIFTEEN-MINUTES!, you can have it..."

     [http://example.com/colons-legal-in-url:]: "Yup, colons are legal in URL!s"

     (your multi-value expression here): "goes great with expressions!"
 ]

Then we stop. We get 4 new parts that are genuinely useful, that make the evaluator more expressive and run common functionality faster. If you find uses in your dialects–and you almost certainly will–so much the better.

The commit for them fixes flaws in the scanner

There were some things you couldn’t make. In Rebol2/R3-Alpha/Red:

rebol2>> first [:a/(b + c)]                       
== a/(b + c):

rebol2>> first [:(a + b)/c]
** Syntax error: invalid "word-get" -- ":"

It might seem like it wouldn’t be that hard to fix, but fixing it is a lot easier if you can just pass through a moment where the head of that block is a GET-GROUP! and then twiddle it into a regular GROUP! later. (That’s how GET-PATH! works when it has a GET-WORD!-looking thing at the head). The scanner is already rather fiddly and throwing in more fiddly-bits is not forward-looking.

This lets that work basically for free, just a minor tweak on the previous method:

>> first [:(a + b)/c]
== :(a + b)/c

>> type of p: first [:(a + b)/c]
== get-path!

type of first p
== group!  ;-- regular GROUP! as first element of GET-PATH!, perfect

They’re awesome, you’ll love 'em

Try them out and let me know what you think, and if you find cool new dialect applications! Some demos:

; GET-BLOCK!, doing a fast REDUCE
>> a: 10
>> b: 20
>> :[a b]
== [10 20]

; GET-GROUP!, getting a PATH!
>> o: make object! [f: 304]
>> path: 'o/f
>> :(path)
== 304

; SET-GROUP!, setting a BLOCK!
>> m: <before>
>> o: make object! [f: <before>]
>> block: [m o/f]
>> (block): [1020 304]
>> block
== [m o/f]
>> m
== 1020
>> o/f
== 304

; SET-BLOCK!, with a block on the right
>> a: _ b: _
>> [a b]: [10 20]
>> a
== 10
>> b
== 20

; SET-BLOCK! with a non-block on the right
>> a: _
>> b: _
>> [a b]: <thing>
>> a
== <thing>
>> b
== <thing>

I’m sure people will think of improvements, but that’s for starters.

And note they’ll get even better with mirror bytes, where (x): won’t cost any more than x: !!! I’m relatively confident I can make that happen, some groundwork is done already.


((Double your Pleasure)) ((Double your Fun!))
((Double your Pleasure)) ((Double your Fun!))
#2

I am curious what in general the idea is for handling get-things at the head of a path, and set-things at the tail. Before this it was just words, but now it can be []s or ()s. In particular, wasn’t it just decided that having a get-word at the tail of a set-path (as in a/:b: 3) is to be deprecated in favour of ()-ing it (as in a/(b): 3)?

This change makes it even more important to clearly specify the rules for what can go into a path and where:
is :[a]/[b]: a get-path with a set-block at its tail, or a set-path with a get-block at its head?

Perhaps this needs its own topic, even …


#3

It has one: immutable paths:

“Since there’s a controlled number of points that can make paths, you can set rules for them (e.g. no fewer than two elements, no paths-in-paths). And since there are no direct modifiers, they can’t be changed to disobey this rule.”

“I’ve wondered if a/(b): c is superior to the point that the path creation rules prohibit embedded get-words. If you couldn’t put any GET-SET-LIT inside path elements, it could stop ambiguities.”

“Furthermore, some types (like FILE! or URL!) have slashes in them. Should inserting them into paths be an error, or at least use those slashes to point out where path segments are and split along them?”


#4

I was looking at the systems.r table, and noticed this:

Amiga: 1
;-------------------------------------------------------------------------
0.1.01 _ "m68k20+"
    ; was: "Amiga V2.0-3.1 68020+"

0.1.02 _ "m68k"
    ; was: "Amiga V2.0-3.1 68000"

It tries to break things into sections, and considers the SET-WORD! alone to be too slight. Of course, it could have said [Amiga 1]. But it made me wonder, does a SET-BLOCK! look even better?

[Amiga]: 1

0.1.01 _ "m68k20+"
    ; was: "Amiga V2.0-3.1 68020+"

0.1.02 _ "m68k"
    ; was: "Amiga V2.0-3.1 68000"

It might look a bit more divider-y that way, with low-cost and light weight.

With blanks in paths not showing, you get some other interesting concepts:

//Amiga// 1

0.1.01 _ "m68k20+"
    ; was: "Amiga V2.0-3.1 68020+"

0.1.02 _ "m68k"
    ; was: "Amiga V2.0-3.1 68000"

Pretty weird, but one of those things about seeing outside “The Matrix” of programming…to where you get to paint/mold/sculpt with code in ways Lisps won’t let you…yet have it be machine processable in the same vein…


#5

Whaoah! Blown away! Just when you think Ren-C is at ultimate tensile strength, @hostilefork superheats it into a plasma! Amazing!

Deep lake? This ain’t even a lake we’re talking about anymore!

“It may be only a matter of time before an even deeper part of our lake is found. And who knows what we may discover there.”


#6

Perfect. Sorry I couldn’t find it (the word immutable didn’t connect enough for me). I will continue this topic there.


#7

Glad you like it :slight_smile: but compared to generalized quoting and how it interacts with and preserves binding, this particular change is “trivial” (but let’s see Oldes and Red do it, I’m waiting…)

I’ve tried to be as much of a bean counter as I can, to the extent of embracing-and-extending Carl’s micro-optimization urges. And while “micro-optimization” has a bit of a pejorative tone to it, we must remember that getting to the core of the essential complexity of any system is something you could think of as “optimization”, and getting to the core of it may be “micro-optimization”.

If you look at how the bytes in the type table are lining up, you can see a lot of interesting relationships. Numbers that aren’t picked randomly. If you subtract 2 from the ID for a BLOCK! you get a GET-BLOCK!, if you subtract 2 from the ID for a WORD! you get a GET-WORD!. If you’re wondering why it’s subtraction and not addition with BLOCK! the lower value, it’s because the value of BLOCK! has to be higher than that of GET-BLOCK! and SET-BLOCK! to push it across the comparison line for types that need evaluation, so there’s a quick < or > test for “do you need evaluation”.

Long story short: I’ve actually taken the “micro-optimization” tendencies of Carl and pushed it to all the way up to 3.

(Well, 11. In binary. :-P)