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