well, first thoughts I had… we could have our equivalent of ‘futures’ be, well, a list… and then have results added to the future-list as they become available.
however, I think that makes it fairly inconsistent: if you utilize that list anytime between the start and end of execution, you’ll get some ‘unfinished business.’ It won’t have everything it might have later… meaning how your operation plays out depends on exactly when it happens to look at the list.
essentially, the key to Paws’ system now is that execution splits: at the routine call. An alternative to ‘futures’ (which are restricted to one value… very un-paws-y), or the above idea (a self-populating futures- list)… is perhaps to move that “execution split” later into the execution. We could make this concept of ‘a place to which the routine returns’ first-class, in and of itself… and then any place that blocks against it (that is, any place that absolutely needs its real value), is a place where execution stops, and then split-resumes whenever a value is returned.
what this would mean: a single path of execution from the routine call… possibly, itself, forking in various places… and then, at all of the terminations of that path, places where things blocked against that first-class “split object.” At those places, execution would split again, for every value the routine returned. I’ll call this “first-class ghosts” to differentiate from futures.
BUT what about the problem with the other solution? consistency? specifically, we need to ensure that whatever happens if the value of our ‘first-class ghost’ is accessed or utilized anytime during the execution of the program is the same as any other time. Specifically, if it’s accessed prior to the completion of the originating routine, the eventual result of the program needs to be identical to as if the accesses happened to come to pass after the completion of the originator.
Hence, if we use these “first-class ghosts”… they have to operate identically no matter when they’re utilized… which means that program execution has to fork wherever they are used, for every element they’ve ever had! i.e. then that our “first-class ghosts” become a sort of first-class fork in the program execution: anywhere they are referenced, the program splits into one branch for each value they ever have had, or ever will have.
BUT this is Paws… and we don’t create a tool to do one thing where instead we can create a tool to do multiple
things. So, we want to abstract out this concept o first-class ghosts into a generalized tool, that we can then
re-apply to the original problem… as I said above, these are sort of first-class forks in the program’s
execution. Why don’t we make them exactly that? Hence, we could have a fork
object, that provides a natively-
implemented fork
method. When called, the routine never returns (in the original thread; that is, it is a
forced-blocking
routine, causing the original thread to be deallocated when it is called); and proceeds to
result once for every element of itself (a list
, like anything else in the language). On top of this, we
would change the lookup
on this, libside, such that any lookup is actually preformed on all of the elements
simultaneously, by fork
ing against the elements first.
That means, that any place you use a fork
(looking up values on it, calling methods on it, even comparing
it against anything else that is not, itself, a fork), the program will… well… fork, and then the original
action will happen for each element instead!
This is also, actually quite easy to implement as well: we already have the framework in place with our
execution
s: we just have infrastructure fork fork()
block (that will kill the calling original
execution
), and then have it call the calling execution
once for each of its own elements. (Notice, this is
practically identical to the proposed implementation of list each()
-and that’s by design. The only real
difference is that the fork
object would pass all lookups through fork fork()
).
SO, all this said: how do we map this new, generalized tool, back to our original problem? We simply create a
ghost
type that “inherits” from fork
: it works exactly the same way. However, our ghost
is implemented
natively, and an opaque reference is retained to any execution that fork()
s against it. Then, when new
elements are “resulted” into our ghost
at a later date (i.e. the originating function calls forward with new
values), every place that had forked on our ghost
will now fork off a new execution again for the new
value.
LONG STORY SHORT: ghost
s are branching-pipelines, between the originator routine and the places where they
are used. Every place in the program that uses a first-class ghost
gets forked-at for every value ghost
has
had or ever will have.