The WHILE Object Is Declarative Now
In the recently released version 0.28.14
we’ve changed the iterating algorithm of the
bool.while
object. Until now,
by our mistake, it was imperative. Now, it’s
declarative.
The difference is in the result of its dataization.
The previous imperative version was “returning” a data object.
The new declarative one returns the latest body of the loop (without dataization!).
The difference is huge (thanks to it, many of our tests broke).
This is how the iterating algorithm works now:
while($, ^, x):
var last := TRUE
var i := 0
loop:
if (not dataized(^)) break;
dataized(last);
last := x(^ := $, α0 := i);
i := i + 1;
return last;
Here, ^
is the parent of the while
object, dataization of which returns a boolean value;
$
is the while
object itself;
and x
is the object that is encapsulated
by the while
object — the body of the loop.
Consider this simple loop:
memory -1 > x
while. > w
x.lt 1
[i]
x.write i > @
It is equivalent to the following:
memory 0 > x
seq > w
x.lt 1 # TRUE (x=-1)
x.lt 1 # TRUE (x=-1)
x.write 0
x.lt 1 # TRUE (x=0)
x.write 1
x.lt 1 # FALSE (x=1)
x.write 2
This may look counter-intuitive, but only because you may be used to imperative loops in Java or Python, where variables are mutable and evaluations are “eager.” In EO we have the opposite paradigm: variables are immutable and, more importantly, evaluations are “lazy.” Making the body of the loop affecting the condition of it — this is what conflicts with the laziness nature of EO.
An imperative algorithm checks the head of the loop (the condition) and then,
if the head was true, it evaluates the body. If the head was false, the algorithm
exits and returns nothing. This is how the while
statement
works in C++, Java, and other object-oriented imperative languages.
To the contrary, expecting the algorithm to be declarative and lazy would entail it 1) to return the result of the last evaluation of the body and 2) to not evaluate it. In other words, a declarative and lazy loop equals to its body, evaluated until the head is false.
The new declarative version of the while
object in EO behaves exactly this way:
it dataizes the body only when the result of dataization may be ignored.
The result of the last dataization in the loop
is important because it is what the while
object “is” — it is the body
of the loop after all pre-exit dataizations. In the example above, the x.write 2
is
what the while
object is (and the seq
object too). Since memory.write
is
what it writes into memory, the following holds:
w.eq 2
You may wonder, how is it possible to rewrite this code in order to make its flow of dataizations be the following (a traditional imperative expectation, which is almost what we’ve had in EO before the recent changes):
memory -1 > x
seq > w2
x.lt 1 # TRUE (x=-1)
x.write 0
x.lt 1 # TRUE (x=0)
x.write 1
x.lt 1 # FALSE (x=1)
nop
The following code would work:
memory -1 > x
memory 0 > i
while. > w2
[]
if. > @
x.lt 1
seq
x.write i
i.write (i.plus 1)
TRUE
FALSE
nop
Here, we abuse the design of the while
object: the body is nop
, while the
entire algorithm of the body and the condition are placed together into the
condition. Don’t do this. But it works.