The mixed lazy and strict semantics of the reference interpreter are difficult to reason with, but sometimes have useful applications.
Since exceptions are lazily evaluated, this enables dataflow-dependent exceptions, through error-carrying numbers.
We can have a very basic algebra for combining errors: A guard is a tag, that
produces an exception when it is evaluated, effectively
enum Guard { Success, Fail }
. Guard::Success
is constructed with push 0
(SSSL) and Guard::Fail
is constructed with push <empty>
(SSL).
Guard::and
is simply add
, which lazily combines two guards. Guard::unwrap
is retrieve drop
, to force evaluation of the guard, which is at the top of the
stack, and produce an exception if it has Guard::Fail
.
I don’t think there is a way to inspect values, to see if an error is contained,
so Guard::or
would not be possible without using a boolean flag, in which
case, there is no use for the guard, as it can be trivially derived from the
flag with a branch.
A higher-level data-carrying Result
would be a simple wrapper over a guard,
generated by a compiler.
To force eager evaluation of a lazy expression and produce its value or
exception, while avoiding IO effects, store
, jz
, or jn
can be used.
store
eagerly evaluates its address, so retrieve the value at that address,
then store it back, so it is not clobbered. To avoid retaining references to the
prefix of the heap via lazy retrieve
s, when running with the reference
interpreter, use address 0.
With sub
:
^ - 0 retrieve store
With mul
:
0* 0 retrieve store
The subtraction variant is encoded with 16 + z bytes and multiplication with 16 + 2z, where z is 1 for implementations that require a sign bit for zero values and 0 otherwise.
Conditional branches jz
and jn
eagerly evaluate their condition. Simply
branch to the next instruction in both cases.
With jz
:
jz .next
.next:
With jn
:
jn .next
.next:
These both require 6 + 2l bytes, where l is the number of bytes in the
label. If labels are assigned in increasing numeric order, this will be shorter
than the sub
variant for programs with less than 32 or 64 other labels,
depending on z.