I am thinking to introduce futures to Felix something like this:

        future x = f (y);
        println$ y;
        println$ x;

In this guise, a future is yet another kind of variable evaluation strategy.
Like a val, a future can be evaluated when the compiler chooses:
either immediately, concurrently, or at the first use. This is also true
right now for a val.

The main difference to a val is that futures cannot use general lazy evaluation:
a future can only be evaluated once, so it's equivalent to lazy evaluation with 
caching.

In the implicit (read-only) form, I propose the following implementation:

        var x : T;
        var chan = schannel[T]();
        spawn_fthread { write$ chan, f y; };
        println$ y;
        x = read chan;
        println$ x;

The main difficulty here is detecting when the read is required.
It may be possible to do this statically -- in the above case this
seems clear. In more general case we would also need a flag:

        var x_flag = false;
        ..
        if not x_flag do
                x_flag = true;
                x = read chan;
        done

This would have to be executed before every use of the variable.

In the explicit form, you have to force the evaluation, which just calls
the above code.

The main problem here is that generators cannot read schannels,
only procedures can do that .. so the code has to be written out every time.
This also means this implementation of futures could never work in a function.
Even if the evaluation did not involve side-effects. [Recall, schannels are
read by doing a service call which is done by *returning* controi
to the run time scheduler, which implies the machine stack must be empty]

However *inlined* functions are always made procedural.
Non-tail-recursive functions cannot be completely inlined.
However recursive functions can be made procedural.

Closures stored in variables can't be inlined either.
This is primarily a typing issue: one could easily make a closure
of the procedural form of a function. 

The procedural form of a function is simply the original function
with an extra pointer argument, and the returns replaced by a store
at the pointer. If the code is reduced to "three address form" by unravelling
expressions and introducing temporary variables, we can see all
functions can be made procedural.

It's actually interesting that because generators which yield only work
if their closure is stored in a variable, but that closure can still be
a procedure if only the type is converted too, and uses are converted.

in fact the ONLY code that cannot be converted is variables of
C function type, i.e. callbacks. Even C bindings to functions are
trivially converted, although one must not that C procedure
bindings still use the machine stack: the contraint becomes
that you cannot do service calls within a callback.

With static analysis some of the checks on use might be eliminated.
The advantage of this method is that the variable appears ordinary.
The disadvantage is that the compiler has to replace all uses of the 
variable, in particular this would be difficult to do right if the variable
could be addressed (unless you count that as a use). 

****

There is another implementation, using objects.

interface future[T] {
  set: 1 -> 0;
  get: 1 -> T;
}
object InTheFuture[T] (user_set: &T -> 0) implements future  = {
  var flag = false;
  var value: T;
  method proc set () { user_set$ &value; flag = true; }
  method gen get () { if not flag do set(); done return value; }
}

looks tempting (it just will NOT work if the user_set tries
to read an schannel!)

This can actually be fixed by adding a procedure force.
That does the read if the flag is not set and sets it.
It does nothing if the flag is set already.

The get method checks the flag and ABORTS if you haven't
forced the read yet.

The USER can then write a generator:

        inline gen getit () { obj.force(); return obj.get(); }

which cannot abort. .. this function will be inlined
(because it says inline) and it will be lifted out
of expressions (because its a generator): you just can't
make a closure out of it and expect it to work.

[Note .. sorry for the confusion .. but only yielding generators
have to be closures! Because only yield implies the generator
has to be resumed. So there are really two kinds of generator,
those that yield and those that don't]

--
john skaller
skal...@users.sourceforge.net
http://felix-lang.org




------------------------------------------------------------------------------
Monitor your physical, virtual and cloud infrastructure from a single
web console. Get in-depth insight into apps, servers, databases, vmware,
SAP, cloud infrastructure, etc. Download 30-day Free Trial.
Pricing starts from $795 for 25 servers or applications!
http://p.sf.net/sfu/zoho_dev2dev_nov
_______________________________________________
Felix-language mailing list
Felix-language@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/felix-language

Reply via email to