Dissecting R3-Alpha's EVENT!: What Should We Think?

Quick Retrospective Look At EVENT!

Not everyone had to deal with EVENT!. But it's relatively easy to understand as a tiny and very-limited OBJECT!. Here's what one being created looked like in R3-Alpha's %prot-http.r

make event! [type: 'read port: http-port]

It looks a bit like a MAKE OBJECT!, with SET-WORD!s and expressions. But what actually happened under the hood?

Datatypes in R3-Alpha had "MT" functions (e.g. MAKE TYPE). So if you wanted to MT_Event() it would take in a BLOCK! and then immediately call a function called Set_Event_Vars().

The code is pretty short:

REBVAL *var;
REBVAL *val;

while (NOT_END(blk)) {
    var = blk++;
    val = blk++;
    if (IS_END(val))
        val = NONE_VALUE;
    else
        val = Get_Simple_Value(val);
    if (!Set_Event_Var(evt, var, val))
        Trap2(RE_BAD_FIELD_SET, var, Of_Type(val));
}

It walks through the block, gathering two items at a time. (If it only sees one item without a second, it uses a global NONE! value as the second.)

Immediately you notice that this is going to be a much more "scant" form of evaluation than in MAKE OBJECT!. If you weren't tipped off by the calling of a function named "Get_Simple_Value()" then you could notice that if you are only processing two values at a time, that's going to rule out things like

make event! [type: second [read write]]

...because it would only see type: second in the first pair, and then [read write] #[none] in the second.

It's worse than it sounds. Let's jump directly to Get_Simple_Value() to look at its implementation:

if (IS_WORD(val) || IS_GET_WORD(val))
    val = Get_Var(val);
else if (IS_PATH(val) || IS_GET_PATH(val)) { //val = Get_Path_Var(val);
    REBVAL *v = val;
    DS_PUSH_NONE;
    Do_Path(&v, 0);
    val = DS_TOP;
}
return val;

It basically does a "GET" of a WORD! or PATH!. But there's no evaluation. So if you say:

get-type-word: does [either reading ['read] ['write]]

make event! [type: get-type-word port: http-port]

It won't call the GET-TYPE-WORD function. It will GET the GET-TYPE-WORD function, and try to set the TYPE: to that.

Other values act similarly perplexingly...whether it's a GROUP!, or whether it's a LIT-WORD! that doesn't get evaluated. So the Set_Event_Var() function has to accept LIT-WORD!s as well as WORD!s, making things just a little bit more confusing.

Why Is MAKE EVENT! So Much "Lousier" Than MAKE OBJECT! ?

What MAKE OBJECT! does is it gathers all the SET-WORD!s in a block up and creates an object with those keys. Then it binds the block you gave it to that object, and runs the code. Since the set-words are all bound to the object, it just naturally works that the evaluation of ordinary expressions will sink the evaluative results into those words.

But the idea behind EVENT! was that it would be a very compressed data structure, so small it fit into one value cell. This means that when it stores that TYPE: it isn't storing a pointer to an arbitrary word symbol...it's storing a small number from a finite list.

So TYPE can't be "bound" in a conventional sense. There's no cell in a context to bind the word to. There's just a packed bit pattern in an EVENT! structure.

You were able to say things like (event/type: 'read) because the way path dispatch worked, only the first element of the path was "bound". It would lookup the event and then send it a message "change the type"...which it would do in its specialized bit packing way. But you wouldn't be able to say do (bind [type: 'read] event) and have that work.

In My Opinion Get_Simple_Value() Should Not Exist

In truth, R3-Alpha did not have to be so stingy with the evaluator. It had the ability to DO_NEXT() in a block, and where it used Get_Simple_Value() it could have done an evaluation. It was there in the pursuit of performance.

But this kind of opens up a bigger field of questioning for the language, just in terms of "What is setting?" and "What is getting?" You might argue that Get_Simple_Value() was some weird anomaly, but here was the behavior of GET itself in R3-Alpha:

r3-alpha>> get (1 + 2)
== 3

r3-alpha>> get quote (1 + 2)
== (1 + 2)

I tried to phrase some questions like "Are GET and SET supersets of PICK and POKE, or do they provide different functionality" and there really isn't an articulation of this.

It's like Rebol made a relatively concrete definition of what a SET-WORD! and a GET-WORD! are, but any understanding of a connection of these things to some functions named SET and GET are kind of unknown.

It's hard whenever looking at the fun parts of playing with Rebol design to come back and face the fact that it's really kind of sitting on a kind of zero-semantics language model. I feel like I'm making some progress on it, but drawing clean lines that people can build on reliably often feels out of reach for this particular medium.

3 Likes