Want to contribute? Fork us in GitHub!

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.