Persistence of Memory: The Enfix Console Trick

Access to the last expression's result is pretty important. So I was wondering what sort of syntax we could use to splice it in. We could commandeer some existing Rebol token:

>> second [<alpha> <beta>]
== <beta>

>> print #1   ; e.g. #1 for last console result, #2 for the one before that...
<beta>

But by definition, anything you pick could be meant literally. We have quite a few more possibilities to make the escaping ever more esoteric, like <console>/#1. But if the point is to keep you from re-entering or retyping a result, having to type in a long thing to get uniqueness defeats the purpose...you're usually trying to save time.

Maybe the best idea would be to find a way to get the result without mentioning it at all. And that gave me the idea of combining enfix and generalized quoting, to make the system/console/dialect-hook (which runs after all the code has been LOADed) do something extremely cool...

Kicking it old-school, calculator-style

The idea is that we inject the last result, quoted, at the start of the code we're running! If the next operation isn't enfix, it will just be scrapped. If it is enfix, it will get picked up as if it had been on the left.

>> 1 + 2
== 3

>> * 10   ; it actually runs `('3 * 10)`
== 30

And with a small tweak to the SHOVE (->) operation, you can now shove the left hand side into a SET-WORD! (or SET-PATH!, SET-GROUP!, SET-BLOCK!....)

>> 1 + 2
== 3

>> -> foo:
== 3

>> foo
== 3

Why is the quoting important? Well, if the last thing you evaluated was an ACTION!, you don't want that action to run, if it was a SET-WORD! you don't want it to assign, etc. etc. But you do want to be able to capture it!

>> func [x] [x + 1]

>> -> addone:

>> addone 10
== 11

Nothing is injected for a VOID! result. So if you ever want to clear out the memory and not get this behavior, just cycle the prompt by hitting enter.

>> 1 + 2
== 3

>>  ; hit enter

>> * 5
** Script Error: * is missing its value1 argument

Are we having fun, yet? :partying_face:

Notable is just how little code this took to implement, mostly comments and adding the SET-XXX! feature to SHOVE. Imagine how easy it would be to start adding your own console customizations!

Not too distant plans might allow things like:

>> data: [<a> <b>]

>> 1 + 2
== 3

>> -> (=> append data)
== [<a> <b> 3]

That would be a shorthand for:

-> specialize 'append [series: data]

I haven't really figured out what these "by example" specializations will look like. At first, I thought it could be a behavior of GET-GROUP!:

-> :(append data)

But if it's not the only behavior of GET-GROUP!, I fear it might be too confusing. Having the behavior of an unfinished call in a group producing a specialization could be unpredictable-seeming, when the user would have liked to get an error. (That is if they could use GET-GROUP! for other things besides specialization, e.g. :(x) as get x)

The average person would probably find that too convoluted and just use a variable. :slight_smile:

1 Like

This is a cool trick, and was fun to try out...

...but I think we should leave it as one of those "optional things" that people might try in their own console configurations. There's too much potential for confusion when people are learning the language.

For reference, here's the code to put into your console's DIALECT-HOOK:

    ; We put the last-result into the code stream, so you can say:
    ;
    ;     >> 1 + 2
    ;     == 3
    ;
    ;     >> * 5
    ;     == 15
    ;
    ; https://forum.rebol.info/t/1071
    ;
    if (not empty? b) and [not void? :last-result] [
        insert/only b quote :last-result
    ]
1 Like