A concrete test of isotopes etc

Take one of the most difficult Rebol programs you have written and translate it, using an LLM, into a minimal Forth-like computational core.

Why Forth-like? Not because Forth is fashionable, but because the execution model can be made extremely small: stack operations, arithmetic, memory, branches and calls. Such a core can be given precise mathematical semantics and its interpreter/compiler can in principle be mechanically verified. That gives us a simple, independently checkable foundation rather than another complicated language.

The LLM's job is then to translate the Rebol program onto that foundation.

Whenever the Rebol source does not determine the intended behaviour unambiguously, the LLM must stop and ask the author:

“What do you intend this construct to mean here?”

The answer must then be converted into an explicit machine-checkable rule, type, invariant, contract or new primitive.

Crucially, we do this initially without teaching the LLM the atomic/isotope/molecular model.

Then we record what semantic distinctions the translation actually requires.

The test is this:

Do the language constructs represented by the atomic/isotope/molecular model reappear independently because they are necessary to translate real Rebol programs correctly?

If they repeatedly emerge, and introducing them reduces ambiguity or simplifies the verified translation, that is evidence that those constructs represent genuine computational distinctions.

If the same programs can be translated completely using the small core plus ordinary types, contracts and a few explicit rules, then the additional atomic/isotope machinery has not yet demonstrated that it is necessary.

So this is not an argument about whether the model is elegant.

It is an experiment:

Start with the smallest provable machine, translate a genuinely difficult Rebol program, let the LLM ask whenever meaning is unclear, and see which of the proposed new language constructs the computation itself forces us to recover.

I think I should make the proposition clearer.

I am not proposing that Rebol be rewritten as Forth.

I am proposing that Rebol be given a very small universal machine underneath it—a Forth-like stack machine whose operations and state transitions are simple enough to specify mathematically and ultimately verify mechanically.

We then take one of the hardest real Rebol programs and ask an LLM to translate it down to that machine.

Whenever the translation is unambiguous, it proceeds automatically.

Whenever the Rebol source does not contain enough information to determine the intended behaviour, the LLM stops and asks you:

“What do you want this to mean?”

Your answer is then turned into an explicit type, invariant, rule, contract or other machine-checkable semantic construct.

The interesting experiment is what happens next.

We do not initially teach the LLM your atomic/isotope/molecular model. We let the problems in real Rebol code tell us which additional concepts are actually required.

If, during translation, the same semantic distinctions represented by your isotopes repeatedly have to be rediscovered, then we have independent evidence that those constructs are genuinely useful parts of the language.

If they are not required, or simpler conventional constructs handle the same cases, we learn that too.

So the proposition is:

Keep Rebol as the expressive language, place a minimal formally defined universal machine beneath it, and use LLM-assisted translation to discover empirically which higher-level language constructs Rebol actually needs.

I think I would now modify my proposed experiment.

Previously I suggested putting a small Forth-like universal machine beneath Rebol mainly as a neutral translation target, and then using an LLM to see which of your isotope/antiform distinctions had to be rediscovered.

I still think that would be interesting, but I now think I was asking too narrow a question.

Why should the machine underneath Rebol be merely a target?

Why shouldn't it also be part of Rebol's extension architecture?

Suppose Rebol normally operates at its present high expressive level, with functions, blocks and dialects.

But underneath it is a very small, formally specified machine exposing the primitive operations required to manipulate evaluation, control flow, state, values, binding, continuations or whatever the evaluator itself ultimately needs.

Ordinary Rebol programs would almost never see this layer.

But when somebody wants to construct a language feature that cannot be expressed cleanly in ordinary Rebol, they can deliberately drop down one level, implement the missing semantic mechanism there, package it as a Rebol construct, and return to the normal language.

In other words, instead of asking:

"How do we redesign the Rebol value model so that every possible future control mechanism can be represented naturally within it?"

perhaps the question should be:

"What is the smallest general machine Rebol needs underneath it so that new language mechanisms can be constructed without redesigning the whole semantic universe each time?"

That gives a different concrete test for isotopes.

Take a representative collection of the problems that motivated isotopes, antiforms and the other Ren-C semantic machinery.

Implement them in two ways:

A. using the isotopic semantic model;

B. using ordinary Rebol plus a deliberately exposed minimal lower-level machine.

Then compare the total cost:

  • complexity in the core
  • complexity exposed to ordinary programmers
  • number of special concepts
  • composability
  • ease of defining new control structures
  • compatibility with existing Rebol
  • and how much privileged evaluator machinery remains.

If isotopes still give the simpler and more general system, that is strong evidence for them.

But if a small programmable substrate solves the same problems without requiring the high-level value model to carry all of that machinery, then perhaps Rebol has been trying to solve the problem at the wrong level.

Forth is interesting to me here not because I want Rebol rewritten as Forth, but because Forth demonstrates a different principle:

the machinery used to construct the language can itself be made available to the programmer.

Perhaps the real missing feature is not another semantic atom.

Perhaps it is a staircase into the basement.

The most difficult program I have written in usermode, at this point, is UPARSE.

https://raw.githubusercontent.com/metaeducation/ren-c/refs/heads/master/src/mezz/uparse.r

Things have atrophied a bit on the testing front, and it's always been in flux, but the tests demonstrate the ambition and scope... trying to give each combinator its own test file:

https://github.com/metaeducation/ren-c/tree/master/tests/parse

I'll draw attention in particular to the "examples":

https://github.com/metaeducation/ren-c/tree/master/tests/parse/examples

I guess if any AI is going to understand that all well enough to translate it into Forth, it might think about helping with the design questions of the system in general and improving the Ren-C.

Nativizing the combinators is something I tinkered with in the past, and I had a concept that instead of a monolithic %uparse.r file being the "source"... instead, each combinator would have its own C source file in which the usermode version would be encoded in a comment... and maintained alongside the C implementation. Then %uparse.r would be a file that the build process generated for those who wanted to have a usermode version to experiment with and hack on if they wanted.

I'm in favor of people looking at the ideas and poking holes in it. But the way it works is (IMO) pretty slick, and isotopes are front and center.

How well this could be translated into Forth I don't know, but it's at least something that can be compared to other parser-combinator-systems. I believe there's a lot of novelty in the ideas, but seeing them translated into another substrate would be cool.

UPARSE is a very good challenge.

In fact it's better if you choose the test, since I don't want to select
something conveniently easy.

The only qualification is that R0 is intentionally still tiny — it doesn't
yet have Ren-C's datatype/string/object library — so reproducing the whole
current UPARSE would mostly test library completeness rather than the
architectural proposition.

Could you nominate the smallest UPARSE mechanism which you believe actually
depends on the Ren-C machinery we're questioning?

For example, perhaps:

  • a user-defined combinator;
  • that receives/uses another parser without knowing its implementation;
  • can succeed/fail/backtrack;
  • can synthesize a result;
  • and composes with other unaware combinators.

We can specify its observable semantics first.

Then, after the generic RAW/S1 trapdoor is complete, I'll give that
specification to the implementation AI with these rules:

no change to R0 evaluator
no change to frozen S1
no new HOST semantic primitive
ordinary R0 + generic RAW only

If it can't be done, that is useful evidence for your argument.

If it can, we have a concrete comparison of the two architectures.

Pick the nasty bit. :slight_smile:

While waiting for your UPARSE challenge, I tried another test of the same architecture.

I froze the R0 evaluator/trapdoor and asked whether a useful debugger could be built without adding debugger semantics to the evaluator.

It can.

The debugger policy is ordinary R0. It descends through the existing generic RAW trapdoor only for the genuinely low-level operations: capture execution state, inspect contexts/frames, restore state, and resume.

It can suspend code inside nested unaware callers, inspect locals and activation frames, inspect captured closure state, resume correctly, and treat the suspended state as a first-class HLL value. Ordinary R0 functions and recursive closures can themselves run while the debuggee is suspended.

I initially had a nasty address-dependent corruption bug. It turned out not to require any new evaluator machinery: I had accidentally allowed debugger and debuggee to allocate from the same heap while both were alive. Giving the two execution worlds separate heap regions fixed it.

The hardened debugger now survives 100 suspend / inspect-with-R0-closures / resume cycles cleanly.

Current result:

  • 156 checks
  • 0 failures
  • no bad opcodes
  • no unbound-word dumps
  • no hangs
  • frozen R0 evaluator/trapdoor unchanged
  • S1 unchanged
  • HOST unchanged
  • no eighth primitive added

The debugger examples are in:

examples/debugger-basic.r0
examples/debugger-frames.r0

So this is another data point for the architectural proposition I was trying to test: when the HLL reaches something it cannot conveniently express, a sufficiently small generic lower-level interface may let the HLL build the facility itself, without teaching the evaluator about that facility.

UPARSE is still the more interesting test because you chose it. I'm leaving that baseline frozen until you give me the particular behaviour you think depends on Ren-C's machinery.

While waiting for your UPARSE case, I tried taking the same frozen R0/S1 system somewhere more practical.

It now runs in a browser as WebAssembly.

Live demo:

The application logic running inside WASM is R0:

counter: 0

on-click: [
counter: + counter 1
web-set-int 1 counter
]

Clicking the button calls into WASM, R0 maintains and increments the counter, and R0 requests the DOM update. JavaScript is just the browser boundary: it forwards the click and maps R0's numeric DOM handle to the actual HTML element.

There is no REPL involved. The R0 program is loaded as an application and executed by the machine.

The interesting result for me is that the existing R0 evaluator and S1 compiled to WebAssembly unchanged:

  • evaluator/trapdoor unchanged
  • S1 unchanged
  • HOST unchanged
  • no new primitive
  • no WASM-specific language semantics

The current .wasm is about 43 KB raw and 15 KB gzipped, without any serious size optimisation.

I've put the ordinary R0 application source directly on the live demo page. The low-level RAW/S1 adapter is hidden underneath, so you can open it if you want to see how the R0 level drops through the trapdoor to the host.

This is beginning to clarify what I think the project may actually be useful for.

Classic REBOL put a great deal of emphasis on rapid interactive programming for a human at the console. I'm increasingly interested in the complementary case: a small REBOL-family language intended to be generated, loaded, inspected and executed by machines, with the lower substrate available when the high-level language needs to manufacture something it doesn't already have.

A sort of REBOL for ROBOTs.

UPARSE remains the adversarial test, of course. I'm still waiting for you to choose the particular behaviour you think the frozen architecture shouldn't be able to reproduce.