HELP EXEC                                    Jocelyn Paine November 1992


This module provides facilities for writing brains for Eden that, as far
as they are concerned, run continuously, rather than starting afresh on
each call to -think-. In effect, it provides a simple form of coroutine.
Experienced Poplog programmers will see that it is a simple interface to
LIB PROCESS (see HELP PROCESS): I have written it so that novices can
write co-routine style brains without having to know anything more about
LIB PROCESS.

You can load it by doing
    lib exec;


Introduction
------------

Here's an example, which you can try by running EXEC_BUG.P.

    needs exec;

    vars co;

    define start_thinking();
        proc_to_process( brain ) -> co;
    enddefine;

    define think();
        restart_process( co );
    enddefine;

    define brain();
        repeat
            if smell() = "here" then
                exec( "grab");
                exec( "use" );
            else
                exec( smell() )
            endif;
        endrepeat;
    enddefine;

    define bugdead();
        start_thinking();
        "rerun";
    enddefine;

This is a bug which heads towards the food until in the same square,
same square, then grabs it, and then uses it. In doing this, it's like
the bug in VERY_SIMPLE_BUG.P. However, it uses processes, and so
demonstrates the use of EXEC.P. The call to -needs- just loads this
library: see HELP NEEDS. You could have used -lib- instead, but
-needs- avoids loading it more than once.

The ``brain'' procedure is -brain-. You can see that it runs
continuously in a repeat loop, and calls -exec- everytime it decides to
obey an action. This is quite unlike the brain in VERY_SIMPLE_BUG.P,
which starts afresh on each call to -think-.

Let's look at the code in more detail.
    if smell() = "here" then
        exec( "grab");
        exec( "use" );
    else
        exec( smell() )
    endif;
If smell() is not "here" (-else- statement) then it will be a direction.
These directions have the same name as actions, and so obeying them will
cause the bug to move nearer to the food, or at least to face towards
it. This is how VERY_SIMPLE_BUG.P works too.

If smell() is "here", then the bug can grab the food. Now, once it's
done that, it can safely use it on the next action. So it's nice to be
able to write
    exec( "grab");
    exec( "use" );
These two actions are separated by one simulation cycle. But by using
-exec-, we can ignore that, and pretend that one occurs immediately
following the other. The first -exec- returns control to the call of
-restart_process-, which was called from -think-. This returns to the
simulator, which finishes the cycle, starts the next one, calls -think-,
and re-starts -brain-. And so we get to the second -exec-. Incidentally,
this idea can be used in many other contexts than Eden, and coroutines
are a wonderful tool for structuring difficult programs. See HELP
PROCESS for more info.      

Eden, of course, still expects its brain to be called -think-. So how do
we interface -brain- to -think-? The answer is, via the code in
-start_thinking-. This makes -brain- into a process or coroutine
by calling
    proc_to_process( brain ) -> co;

This process is held in the global variable -co-. Each time -think- is
called, it restarts -co- by calling
    restart_process( co );
which causes causing -brain- to run until it calls -exec- to return an
action.

Even if you're not sure what exactly is going on, you can imitate this
pattern for more complex brains. Just replace the code inside -brain- by
your own code. You don't need to have the entire brain in one routine:
you can call -exec- from subroutines, as in this rather contrived
example.

    define brain();
        repeat
            if smell() = "here" then
                grab_it();
                eat_it()
            else
                move_to_it()
            endif;
        endrepeat;
    enddefine;

    define eat_it();
        exec( "use" );
    enddefine;

    define grab_it();
        exec( "grab" );
    enddefine;

    define move_to_it();
        exec( smell() );
    enddefine;


Reincarnation and -bugdead-
---------------------------

If you intend your bug to reincarnate, it's important to know how to
reset its brain. The point is that, once it's eaten the food in the
example above, the procedure -brain- has been ``used up''. You somehow
need to set it back to the start before it can be used again.

This is done via the code in -bugdead-. This calls -start_thinking-
before returning a result. This ensures that on reincarnation, -co-
contains a new brain process which, once again, would run from the
beginning.


Exported predicates
-------------------

PUBLIC goal_to_process( Goal+, Co- ):

Converts Goal into a process, returned in Co.


PUBLIC restart_process( Co+ ):

Runs Co until it calls exec(Action). Action is then returned as the
second argument of 'restart_process', and Co suspends itself, as
described above. Co can either be a process which has not been run
before, or one which has, and is suspended.


PUBLIC exec( Action+ ):

Called from inside Goal to suspend and return an action.
