Ideas from the Lab: ⚗️ Unbound SET-BLOCK! object keys?

The new-age TUPLE! and PATH! won't let you include types that would cause ambiguity. For instance:

>> to path! [foo]
== foo/

>> to path! [foo _]
== foo//

>> to path! [foo: _]
** Error: can't put a SET-WORD! inside of a PATH!

I'm not sure exactly how many types to allow or disallow. But suffice to say not everything will be supported.

But among important things that will be supported are plain GROUP! and plain BLOCK!, at any position, including the head (...which R3-Alpha and Red don't support in scanning or the evaluator, though you can build any path you like with any values...despite the enormous downsides).

We know pretty well what a GROUP! in a PATH! does. But what should a BLOCK! in a path do?

Today it acts the same as if it had been in a group:

m: make map! [[a b c] (1 2 3)]

>> key: [a b c]
== [a b c]

>> m/(key)
== (1 2 3)

>> m/[a b c]
== (1 2 3)

>> m/([a b c])
== (1 2 3)

That looks reasonable, although it seems not to buy all that much on the surface.

But looking closer at it, I noticed this makes maps a very tempting way to get names of things with spaces in them:

>> m: make map! compose [
    [a cool name] "some value"
    [<crazy> #name] (func [x] [print [x + 1]])

>> m/[a cool name]
== "some value"

>> m/[<crazy> #name] 10

And we're now talking about a smoother syntax with the tuples:

>> m.[a cool name]
== "some value"

That's just kind of nice. So you start to wonder...with the dawn of SET-BLOCK!, outside of efficiency, is there any real technical reason why objects couldn't allow BLOCK!s as keys...and be more competitive with JavaScript and the like?

The technical issues

So one reason for saying that this should only be done with BLOCK! (as opposed to TAG! keys and such) is that BLOCK! has a SET-BLOCK! form. This means an object might say:

obj: make object! [
    [a cool name]: "some value"

Right now SET-BLOCK! is being used for multiple return values, but it could be done with SET-GROUP!...which actually might make more sense in light of recent semantic thinking about groups...

(x y): some-multireturn-func a b c

Obviously you don't want a block on its own to invoke a function or evaluate to something else. And there would be big performance and logistics problems if these tried to participate in the binding process.

But what if they didn't participate in the binding process, and they were just names? Putting things in a block could be a way of declaring data members that deliberately skip out on binding.

The exception would be that when MAKE OBJECT! walked over the body and saw the SET-BLOCK!, it would have to inject a binding onto it...destroying any other binding bits it had. So you'd be talking about a, cool, and name all being unbound in that set order to make room for the binding of the block to the object, making the set-block work. I suppose GET-BLOCK! could work in a similar way when used in such a context, but you'd not be able to dispatch functions from it.

This Once Again Calls into Question MAP! vs OBJECT!

People have always been somewhat skeptical of why maps and objects are different, and what exactly the hard system requirements are that mandate OBJECT!s strictness. I know how things work today but I think it's worth having a go-over to see if there might be a better angle.

1 Like

I'll mention that for function calls, I was thinking it would be nice if it could name a refinement and a value.

>> data: '(a b c)
>> append/[dup: 1 + 2] data <foo>
== (a b c <foo> <foo> <foo>)

Given how common this would be, it's probably better to just use a WORD! instead of a SET-WORD! and people will get the idea, not expecting DUP to be a function call or something of that sort:

>> data: '(a b c)
>> append/[dup 1 + 2] data <foo>
== (a b c <foo> <foo> <foo>)

Or maybe this is a job for TUPLE?

>> data: '(a b c)
>> append/dup.(1 + 2) data <foo>
== (a b c <foo> <foo> <foo>)

Just more stuff too think about (!)