Subject: Re: (not enough) fun with pass()...LONG!!

From: Andrew Wilson <[email-masked]>
Date: Sat, 30 Jan 1993 15:19:24 -0800

Amy sez:

> I want to make a room which inherits from nifty_room, but doesn't use
> nifty_room's look_self method.  So the natural thing to do is to put a
> look_self method on that room.  But the issue is, I still want to use other
> methods further up in the hierarchy.  Basically, I want to pass to 
> two levels up, skipping the look_self on nifty_room but getting
> the methods on room and the parents of room.  Is this possible?
> How do I get around this problem?

Mmm, yep, well the sort of thing you want is called (amongst other things)
COERCION.  COERCION is something which:
	a) Is VERY useful and can save you having to copy acres of code
	when you don't reallly want to have to copy.
and
	b) MOO doesn't seem to be able to do.

Here's an example of the use of coercion.  Let's say we have a $player
hierarchy which looks like this:

	$player
	   ^
	   |
	   | (.parent)
	   |
	$slightly_modified_player
	   ^
	   |
	   | (.parent)
	   |
	$heavily_modified_player

And that, in addition, we have typed the following commands:

	@create $player named Andy
	@create $slightly_modified_player named Andrew
	@create $heavily_modified_player named Andrew_Wilson

And, to keep things simple, we'll also set each player's description to be
the same value.

Now let's say that the following verbs are defined:

	@program $player:look_self
	player:tell(this:description());
	.

	@program $slightly_modified_player:look_self
	pass(@args);
	player:tell("However ", this:title(), " has been slightly modified.");
	.

	@program $heavily_modified_player:look_self
	pass(@args);
	player:tell("In fact, ", this:title(), " has been HEAVILY modified!");
	.

So, by looking at each of the players in turn we get:

	look Andy
	He seems to be a pretty much average sort of a guy, don't you think?

	look Andrew
	He seems to be a pretty much average sort of a guy, don't you think?
	However, Andrew has been slightly modified.

	look Andrew_Wilson
	He seems to be a pretty much average sort of a guy, don't you think?
	However, Andrew_Wilson has been slightly modified.
	In fact, Andrew_Wilson has been HEAVILY modified!

Ok?  Well, so far, this is much like what we already know.

Fine, now suppose someone wanted to write a verb to give a brief description
of every player connected to the MOO.  Let's say that they'd also prefer
each description to be in the same $player-like format, without any
embellishment of any sort.  Something like this would be a good place to
start, don't you think?:

	@program me:@describe_everyone
	for Person in (connected_players())
		player:tell(Person:title(), ":");
		Person:look_self();
	endfor;
	player:tell("(finished)");
	.

When run, this verb would give the following output, assuming that the three
players were connected:

	@describe_everyone
	Andy:
	He seems to be a pretty much average sort of a guy, don't you think?
	Andrew:
	He seems to be a pretty much average sort of a guy, don't you think?
	However, Andrew has been slightly modified.
	Andrew_Wilson:
	He seems to be a pretty much average sort of a guy, don't you think?
	However, Andrew_Wilson has been slightly modified.
	In fact, Andrew_Wilson has been HEAVILY modified!
	(finished)

Mmmm, well :@describe_everyone seems to have got everyone's details as requested
but it's a bit confusing to read, because the modified $player's :look_self
verbs have added extra information that the writer of :@describe_everyone doesn't
want to know about.  If only there was some way of telling MOO to call a player's
:look_self verb so that the verb ALWAYS behaved in a certain way, regardless of
how the verb's original semantics had been overriden by redefinitions in
sub-classes.

Too much jargon there for you?  I'm confused certainly, this is like recursion
but with bigger teeth!  Let's re-write :@describe_everyone assuming that there's
a new MOO built-in that we can use, called 'coerce()':

	@program me:@describe_everyone
	for Person in (connected_players())
		player:tell(Person:title(), ":");
		coerce(Person, $player, look_self);
	endfor;
	player:tell("(finished)");
	.

When this new :@describe_everyone is run we get the following report:

	@describe_everyone
	Andy:
	He seems to be a pretty much average sort of a guy, don't you think?
	Andrew:
	He seems to be a pretty much average sort of a guy, don't you think?
	Andrew_Wilson:
	He seems to be a pretty much average sort of a guy, don't you think?
	(finished)

There!  That looks much more like what the verb writer wanted.  We get just the
players' descriptions, without any of the extra stuff that might have been
confusing.  Ok, that was easy because I made the example up, but if this was
really going to work then what would the coerce() built-in actually do?

	help coerce()
	Syntax:  coerce(obj <what>, obj <ancestor>, string <verb>, <args>)
		=> <value>

	Using your imagination, take an object and for a single, foolish
	moment assume that its parent has been changed.  Then call a verb
	which exists on the object, and it will be executed using the verb-code
	for the new parent.  Then assume that the object has, just as magically,
	regained it's real parent again, and carry on as normal!

	In hypothetical MOO-code terms it would look something like this:

		Class = <what>.parent
		@chparent <what> to <ancestor>
		ReturnValue = <what>:(<verb>)(@args);
		@chparent <what> to Class
		return ReturnValue;

	If <ancestor> is not in :ancestors(<what>) then the value E_COERCE is
	returned.  If <verb> is not in verbs(<ancestor>) then return E_VERBNF.
 
Now, there have been several arguments recently in favor of NOT complicating
the poor old MOO any more that it has already been, and in this spirit I suggest
the much more friendly coercion syntax <what>::<ancestor><:verb/.property>.
Using this syntax for a coerced access to an object, the :@describe_everyone verb 
ends up looking like this:

	@program me:@describe_everyone
	for Person in (connected_players())
		player:tell(Person:title(), ":");
		Person::$player:look_self();
	endfor;
	player:tell("(finished)");
	.

It doesn't take a genius incidentally, to find scenarios where coercion might
compromise the integrity of existing classes on LambdaMOO, but this DOES NOT
mean that coercion is worthless.  I happen to think that coercion is a useful
tool and that in a right-thinking world it would form part of MOO's available
abilities.

Finally, by using coercion, Amy would be able to construct :look_self
verbs which COULD perform the tasks she describes.  It would go something like
this:

	@create nifty_room named fabulously_nifty_room
	@program fabulously_nifty_room:look_self
	this::$room:look_self();
	.

What could be more simple?

Andy - Networker

---

From: Greg Hudson <[email-masked]>
Date: Sat, 30 Jan 1993 17:26:16 -0800

> It doesn't take a genius incidentally, to find scenarios where
> coercion might compromise the integrity of existing classes on
> LambdaMOO, but this DOES NOT mean that coercion is useless.  I
> happen to think that coercion is a useful tool and that in a
> right-thinking world it would form part of MOO's available
> abilities.

I will engage my non-genius mental facilities to find a particular
such a scenario: suppose I inherit from "generic fancy player", which
inherits from "generic player".  Generic fancy player overides the
:tell() verb on generic player, adding, among other things, support
for in-server gags.  However, after I "@gag Munchkin" (I've never
spoken with the guy, but he seems to be the butt of most MOO examples
I've seen :) ) Munchkin can still write to me by doing
Player::Greg:tell(text).

(Actually, LambdaMOO implements in-server gagging on the generic
player object, so my example is hypothetical.)

If I were really concerned about this, I believe your model would
allow me to override the :tell() verb on myself, but then I
essentially have to copy the generic fancy player code in order to
guarantee that people don't override it when calling verbs on me.
Overriding all the verbs and just calling pass(@args) wouldn't help,
since my parent would be (in the abstract) temporarily different.  And
I shouldn't have to do either, since I might not even be a programmer.

If you acknowledge the security hole, why do you think coercion is
still a useful tool?  One strength of the object-oriented model--and
of MOO, to the extent that it utilizes it--is that it is very secure
in a multi-user environment.  Even in a single-user environment where
security is not an issue, the object-oriented model aids in localizing
bugs related to data corruption.  Coercion is essentially a blow to
the integrity of the object-oriented model, and thus to its benefits
in both of those realms.

Of course, MOO's security model is more permissions-based than
object-oriented, since opcodes can modify objects other than the
current object, but what there is of the object-oriented security
model should remain, in my opinion.

I also disagree conceptually with calling this 'coercion', since it's
really coercing an object's parent, not the object itself.  I find
this vaguely bizarre--transforming an object into itself inheriting
from an arbitrary one of its ancestors--but that's probably just me.

> Finally, by using coercion, Amy would be able to construct
> :look_self verbs which COULD perform the tasks she describes.  It
> would go something like this:

> What could be more simple?

A less intrusive solution to Amy's problem is to support a pass
expression with an explicit target:

	pass_expr	: PASS '(' argument-list ')'
			| PASS '(' argument-list ')' TO expr
			;

Or perhaps something more restrictive than 'expr' in the second case,
although you're going to have to check at run-time if the object is
actually a parent anyway, because of chparent().

'pass() to' would return E_INVIND if its target is not an ancestor.
The method executed is the same as the method that would be executed
if a message were passed to the target object.  The current object
remains the same, of course, just as with pass().

So, to answer the question, "What could be more simple?", it is:

	pass(@args) to $room;

I don't think there are any security problems.  I could have just
copied the code the target ancestor would have run.  I suppose there
might be a problem if you're using inheritance to write a 'safe
wrapper' around an object you don't let programmers look at or inherit
from.  I can't think of any situation where I'd do this in practice,
though, and since the code is going to run with my permissions on my
object (or with child permissions on child objects), I don't see why
it matters where I get it from.

I'll also point out that some kind of explicit-target pass() will be
necessary if MOO goes multiple-inheritance.  Naturally, coercion would
handle this as well (explicit-target pass() is essentially coercion
with the message recipient restricted to the current object), but in
my opinion, not as cleanly.

As to Andrew's earlier example about :look_self(), I think such
problems will not arise if programmers preserve the interface and
conceptual behavior of parent verbs when writing overrides.

--GBH