Generic Quoting Makes Headway on MAKE OBJECT!

Rebol2/R3-Alpha/Red have a notation for molding out objects:

Red>> obj: make object! [a: 10]
== make object! [
    a: 10
]

Let's ignore for the moment the fact that it's giving a sequence of what looks to be two words and a block to denote a single OBJECT!. What it's trying to do is give you a syntax that looks like what it would take to create the object if typed from source. (Perhaps it would more appropriately be SOURCE OBJ vs. MOLD OBJ)

Little Objects with Big Problems

First, remember that Red's QUOTE is Ren-C's THE (give next item literally).

Now what if you said:

Red>> obj: make object! [g: quote (1 + 2) u: 0 unset 'u w: quote 'word n: none]
== make object! [
    g: (1 + 2)
    u: unset
    w: 'word
    n: none
]

The resulting MOLDing MAKE OBJECT! doesn't get close to what it's trying to capture.

  • In the original object G is a "PAREN!" with (1 + 2). Running that MAKE OBJECT! would give you G of INTEGER 3.

  • We'll assume it meant #[unset] instead of the word UNSET

    • To run as written, it would confusingly pass the UNSET function the next line as an argument. It would do unset w: 'word, thus unsetting word

    • If it had said u: #[unset] instead, the make object! would have failed, as it's not legal to assign unsets via set-word in historical Rebol or Red.

  • W would come out as a WORD! here, instead of the LIT-WORD! in the original object.

  • N here might seem to work, but it only works so long as the word NONE is bound to #[none]. So round-tripping this field depends on the context you run the make object in.

Generalized escaping to the rescue

Ren-C doesn't have a full solution to the problem of round-trip representations as text. But now, when it comes to this, it's at least a powerful step closer.

>> obj: make object! [g: the (1 + 2) u: ~ w: the 'word n: null]
== make object! [
    g: '(1 + 2)
    u: ~
    w: ''word
    n: ~null~
]

Here we see these loopholes getting closed. The make is actually able to produce a structurally identical object (like I said, let's avoid talking about binding, that's its own conversation).

And reconstructing the object is context-independent...no keywords are being used. Note that I threw in a NULL for good measure, to show that there's a representation for that... a quasiform of the word "null"...that evaluates to the antiform of the word null.

The n: ~ is another quasiform... the quasiform of blank ("trash"), which evaluates to the antiform of blank, which represents the state of an unset variable (called "NOTHING"). Assignment of nothing to variables is legal so it wouldn't generate an error to run that (there is a state called NIHIL which is an empty pack isotope, which can't be assigned, but it's not legal to store in variables ever so no need to represent it here...though you could).

It's not necessary to put the apostrophe on all items, only those that evaluate...so the system should probably only add them if needed:

>> obj: make object! [i: 10 t: "hello" p: ''a/b/c]
== make object! [
    i: 10
    t: "hello"
    p: ''a/b/c
]

INTEGER! and TEXT! can be left as-is, so perhaps worth it to do so. Here we see a "LIT-PATH!" (actually a quoted PATH!), escaped in the original representation for its assignment, escaped again for its re-representation.

This generalized-escaping-based mechanism replaces a previous attempt to deal with the issue, and gives strong evidence that the effort was time well spent. :face_with_monocle:

1 Like

It's amusing to see 2 years later that Red is only beginning to discuss the question.

Oldes says:

"To be honest, I'm not sure if it's correct in Red. I think Red's behavior is inconsistent.
I believe that these 3 results should not be same:"

red>> body-of make object! [a: 1 + 1 c: to lit-word! 'a]
== [
   a: 2 
   c: 'a
]

red>> body-of make object! [a: 1 + 1 c: 'a]
== [
    a: 2 
    c: 'a
]

red>> body-of make object! [a: 1 + 1 c: to word! 'a]
== [
    a: 2 
    c: 'a
]

They then try to discuss it, but of course they've rejected the techniques that can resolve it (generic quoting and isotopes).

I'll again point out that there are still fundamental issues with representing bound structures via a text-based round trip that should not be ignored. But, for the basic considerations here are fairly well covered.

There's simply a lot of solutions here bearing fruit, while their understanding has not moved an inch since Rebol2. :man_shrugging:

2 Likes

Another year down the road, they're talking about it again...issue opened May 15th:

`body-of`/`to block! object` are lossy · Issue #5140 · red/red · GitHub

Oldes demonstrates a flicker of awareness, here:

"Ren-C has some concept of multi-quoting. I suppose that you would use object [a: ''x b: 'x], but I don't have access to Ren-C for a try."

:man_facepalming:

1 Like

Speaking a little about binding...

...given how pure virtual binding is evolving, we are now considering BLOCK! to be "evaluative" because it will pick up bindings from the local environment... while quoted things will not. (The previous iteration of pure virtual binding had all values--including quoted ones--getting binding, because binding propagation was done in all structural operations... including PICK and FOR-EACH... leading to a mire of multiply-applied binding that is now being reined in with the consequence of living in a mostly-unbound world.)

To be consistent here, we'll need anywhere a BLOCK is being represented "by value under evaluation" to quote it:

 >> block: '[foo]

 >> foo: <bar>

 >> obj: make object! [b: block]
 == make object! [
     b: '[foo]
 ]

Use your imagination that we're actually in a rich terminal where that block "argument" to the MAKE OBJECT! is a rendering of something that you could get the address of and execute. If you did, you would want that quote on the [foo] block to indicate that it should not pick up a binding.

Thanks to doing string interpolation by putting strings in blocks vs. giving strings bindings, this won't be needed for strings.

1 Like