Skip to main content
added 356 characters in body
Source Link

EDIT: coroutines also have a huge advantage over references in that they dont require the garbage collector. java is an excellent language for normal everyday applications, but its probably not such a good choice for abnormal everyday applications (such as the one that executes when you turn on the machine) where the GC is not likely to be available.

EDIT: coroutines also have a huge advantage over references in that they dont require the garbage collector. java is an excellent language for normal everyday applications, but its probably not such a good choice for abnormal everyday applications (such as the one that executes when you turn on the machine) where the GC is not likely to be available.

Source Link

Implementing Classes as Coroutines?

most OOP languages implement a class as a garbage-collected reference. however, a task (coroutine) based implementation may offer several advantages over traditional object references:

-- method calls are transfer points and not separate functions. as such a coroutine directly
intergrates method calls into its control flow.

--this in turn allows a coroutine type to declare a context-free grammar for valid sequences of method calls which can be statically checked on the server end and is checked at runtime on the client end with a simple parsing table.

-- polymorphism is then implemented simply by intercepting the transfer points. this may provide a much more flexible polymorphism than OOP dynamic dispatch (which gets cumbersome if theres >1 dispatch parametre).

-- a coroutine instance is returned to the caller at runtime as a dialog object which contains the current schema node and exchanges parametres and returns on each continuation point. this object is also a de facto weak reference to the coroutine object (the dialog is allowed to close if its on a final node).

as an example of how such a type might work, ill sketch out a window using a pseudo-ada syntax:

    cotask interface window is
   -- the schema declares a context-free grammar for the valid sequences of method calls 
   -- on a window. transfer points (accept branches) in an implementor must follow the
   -- syntax. this can easily be checked at compile-time if certain rules are applied.

   schema is 
      start = createWindow drawWindow {stuff} closeWindow;
      stuff = doThisWithTheWindow | doThatWithTheWindow;

   -- entries (methods) are treated as terminals in the syntax above. the exported entries
   -- are the alphabet of the schema syntax.

   entry createWindow (int x0,y0,w,h);
   entry drawWindow();
   entry doThisWithTheWindow (P p) return R;
   entry doThatWithTheWindow (Q q) return S;
end window;

cotask myWindow implements window is

   -- we assume a rule that a nonterminal is implemented on a function with the same name
   procedure stuff(out boolean continue) is
   begin
      select
         accept doThisWithTheWindow(p:P) return R do ... continue := true; end;
      or accept doThatWithTheWindow(q:Q) return S do ... continue := true; end;
      else continue := false;
      end
   end stuff;

begin
   -- an accept swaps context back to the caller and continues on the branch body on the
   -- corr entry
   accept createWindow(int x0,y0,w,h) do ...end;   
   accept drawWindow() do...end;
   boolean continue;
   loop
      stuff(continue);
      exit when not continue;
   end loop;
   accept closeWindow() do...end;
   -- the return here closes the dialog and deletes the context. this is only allowed
   -- on a final node such as closeWindow()
 end myWindow;
         
 procedure myWindowClient() is
 myWindow W;
 begin
    W.create(0,0,1024,768);
    W.draw();
    loop 
       W.doThisWithTheWindow(p);
       W.doThatWithTheWindow(q);
       exit when someTwitThrowsABrickThruTheWindow;
    end loop;
    W.close();
 end myWindowClient;
    

polymorphism is implemented by rewriting one or more accept() branches. a type may implement several such interfaces as a union of disjoint schemas (every interface is considered disjoint), and the base types then become an alternation of nonterminals in the implementor type. a function such as stuff() could also be overridden as a type parametre.

the down side of course is that the system must create a context for the coroutine and in general its not possible to determine an optimal storage size for the context beforehand. a method call also requires a context swap which is slower than a simple function call (and theres an additional type validation overhead on the client end too).

does anyone know of a language that does this? either way your thoughts will be appreciated :)...thx