[comp.lang.modula3] Vague design question

nr@.Princeton.EDU (Norman Ramsey) (04/10/91)

I wish to create an object that represents a target program suitable
for debugging.  For simplicity's sake, let me say that the target may
be in one of three states:

	Disconnected:	I have symbol table information but no process
			is executing the target program.
	Stopped:	I am connected to a process that is executing
			the target program but is currently awaiting 
			instructions from the debugger.
	Running:	I am connected to a process that is executing
			the target program and is not listening to the
			debugger.

Now, given methods may or may not apply depending on the state.  For
example, a ``connect'' method applies only to a target in a
disconnected state; only a stopped target can be continued, &c.  
One possible strategy is to use subtyping to distinguish various
states, but since targets can change states, many methods would have
to return new targets (and somehow invalidate the old ones).  Here's
an example of that style:

TYPE 
  T <: ROOT;
  Disconnected = T OBJECT METHODS connect(pid:Pid.T):Stopped; END;
  Stopped      = T OBJECT METHODS continue():Running;         END;
  Running      = T OBJECT METHODS interrupt():Stopped;        END;

Worries about this technique:
  -- clients of the interface must track which subtype they have
  -- all state-changing methods must invalidate the old object,
     and all methods must ensure their arguments are valid objects
  -- what if I can't shoehorn my methods into a single inheritance
     hierarchy?

An alternate technique would be to have a single target type that
includes a state:

TYPE
  State = {Disconnected, Stopped, Running};
  T <: OBJECT
    state: State;
  METHODS
    connect(pid:Pid.T);		(* Disconnected --> Stopped *)
    continue();			(* Stopped --> Running *)
    interrupt(); 		(* Running --> Stopped *)
  END;

Advantages:
  -- clients need not track which state the target is in, provided
     they handle exceptions when operations are applied to the wrong
     state.
Troubles:
  -- all methods must ensure their arguments are in valid states
  -- specification of what methods do to the states might get
     complicated 



I'm leaning toward the second solution, probably just because it's
more like non-object-oriented programming, and problems I encounter
are likely to be familiar ones.  I would appreciate hearing people's
comments about this problem, or about experience they've had with
similar problems.
-- 
Norman Ramsey
nr@princeton.edu

craig@leland.Stanford.EDU (Craig Chambers) (04/10/91)

Dynamic inheritance ala Self would be a convenient mechanism to provide the changing
behavior modes of the single debuggee object.  The interface to clients would be something
much along the lines of your second proposal, but the internal implementation would be
more well organized and factored.  Since M3 doesn't support dynamic inheritance, you 
could simulate it with a wrapper object whose single instance variable was changed from
one internal state to another; the wrapper object forwards messages to the contained
instance variable.

-- Craig Chambers