MOO-cows Mailing List Archive

[Prev][Next][Index][Thread]

Non-overrideable verbs



[...background: there was a discussion on OpalMOO about adding a
    -overrideable flag to verbs which could be reset to prevent 
    descendants from redefining a verb, e.g., to prevent someone
    from defining an object:ancestors() to return something sneaky.

    I thought this might be of interest here, too.
    Also, traffic here's been a bit slow lately, so I thought
    I'd spur some discussion... :-) ]

When considering non-overrideable verbs, I think the real issue here is
control of the namespace.  That is, it's not simply a question of
preventing people from overriding individual verbs but rather of being
able to reserve chunks of namespace real-estate for your own use.  This
goes quite a bit beyond the question of parents being able to block
children from redefining stuff since in some cases it's just as
important to keep someone from using a given verbname in the first
place.

If player A decides to use the verb name :fribble for a given purpose,
someone else (B) who wants to use that name for something different is
just screwed.  If B just goes ahead and uses that name, now there are
two distinct notions of what it means to :fribble an object.  This then
saddles the random programmer with the task of determining whose notion
of :fribble a given random object implements.  Or, worse yet, not being
aware that there are >=2 notions of :fribble in the world, s/he just
assumes one of them and gets it wrong.
  
Verbs (and, likewise, properties) all live in a single global namespace.
FLAT GLOBAL NAMESPACES SUCK POINTY LITTLE ROCKS.

You might think that if the two notions of :fribble belong to entirely
different interfaces, this shouldn't be a problem.  But what if you need
to code up an object that implements both interfaces --- forget the
whole question of trying to implement MI whether in-server or in-DB;
let's say I'm willing to @copy everything.  If the interfaces happen to
coincidentally share a method name, I just lose --- given a verbcall
there's no way to tell a priori which interface the caller is referring
to.

Example:
  Let's recall that for random VR objects in LambdaCore,
    :set_message is used to change a given @MESSAGES mesasage 
    (e.g., .oleave_msg)
  Let's suppose I (stupidly) decide that :set_message 
    on $mail_recipient is to be used to change
    the contents of a given MAIL message.

  Now try making something that is both a mail recipient 
  and a random VR object.  Maybe if we're lucky these functions
  will have different signatures so that one can infer from the
  structure of the argument list which one the caller wanted.
  BLEAH.

Some of this applies to properties as well; and no, it doesn't suffice
that you can make a property -c.  The question here is how you prevent
people from *creating* new properties in your namespace as well as from
trashing your existing properties.

The fundamental idea here is to separate ownership of the NAME from
ownership of the verb/property. 

SAMPLE APPLICATION
==================
(or, how *I'd* do MI, or at least, that bit of MI that I care about)

It has long been suggested by various people that there needs to be a
notion of "interface" together with a way to quickly establish whether a
given object implements this interface.  E.g., every object in an .exits
list should implement an "exit" interface --- the short story is that
requiring $exit as an ancestor isn't flexible enough; this I believe is
the source of most of the clamourings for MI in MOO.

To this I would add that each interface needs its own distinct
namespace.  (There would, of course, have to be a single namespace for
the interfaces themselves, but that's life...).  Thus, rather than
writing

   foo:receive_message(...)
   foo:set_message(...)
   foo:ancestors()

you write

   foo:[]assert("mail","vr","objlib")
   /* raise error if foo doesn't implement these interfaces */
   ...
   foo:[mail]receive_message(...)
   /* call the "receive_message" method of the "mail" interface */
   foo:[vr]set_message(...)
   foo:[objlib]ancestors()

As for how this actually gets implemented, there are lots of choices. 
There's the totally dynamic/stupid route, in which the parser is
jury-rigged to expand the above to

   foo:($interface:get("assert"))("mail","vr","objlib")
   ...
   foo:($interface.mail:get("receive_message"))(...)
   foo:($interface.vr:get("set_message"))(...)
   foo:($interface.objlib:get("ancestors"))(...)

the theory being that $interface.bar:get("fribble") would return some
munging of <string> that is guaranteed not to have been used for any
verb name other than that of the "fribble" method in the "bar"
interface.  This would make more sense if MOO had a symbol datatype in
which one really do (uninterned) gensyms.  Unfortunately we can't, so
some means of actually reserving verbnames is required.

There's also a Small Matter of Tick Usage; the $interface.bar:get(...)
calls should really be done at "compile" time (whenever that is).

Thus, we come to the poor man's solution, in which we have an explicit
reserved prefix for every interface, and the owner of the interface has
full control over who gets to use the verbnames in that interface. 
E.g., the above verbcode is merely

   foo:__1_assert("mail","vr","objlib")
   ...
   foo:__42_receive_message(...)
   foo:__18_set_message(...)
   foo:__12_ancestors(...)

with appropriate hacks to @list and @program so that these show up in
the more readable form above.  The reason I prefer prefixes to suffixes
(e.g., Gelfin's suggested "-o" suffix) is that one could then do

   @verb    #foo:[mail]* tnt rxd
   @program #foo:[mail]
   return $wombat_mail:($[]strip("mail",verb))(@args);
   .
   @verb    #foo:[wombat_mail_client]callback tnt rxd
   @program #foo:[wombat_mail_client]callback
   if (caller != $wombat_mail)
     raise(E_PERM);
   endif
   return this:(args[1])(@args[2..$]);
   .
   ;$wombat_mail:setup(#foo)

so as to "import" the wombat_mail implementation of the [mail] (i.e.,
__42_) interface.  Here, $wombat_mail implements its verbs in terms of
caller and uses :[wombat_mail_client]callback (i.e., :__84192_callback)
whenever it needs to invoke a verb on the host object.  Note that
$wombat_mail isn't itself a valid implementation of [mail] and thus
can't/shouldn't use the [mail] names for its implementations, which is
why we call `:($[]strip("mail",verb))' instead of `:(verb)'.  This
points up the necessity for the notion of being able to free up a
namespace, allowing a "garbage" namespace that anyone can use, etc...

There is one particular difference for property namespaces, namely that
there's no particular danger in allowing anyone who has control of a
given namespace to install a property of that namespace on ANY object
s/he wants; the worst s/he can do is bloat the database, and presumably
whatever quota system exists can take this into account (e.g., you get
massively charged for every property you want to install on #1).  Thus
lots of stuff that is currently kept in parallel lookup tables, could
just be kept on the objects themselves.

Thus $wombat_mail:setup is able to, by itself, install on #foo the
properties

  .[wombat_mail_client]messages
  .[wombat_mail_client]messages_going
  .[wombat_mail_client]kept_msgs

that it needs in order to function.

You *don't* want to do this with verbs; i.e., allowing a namespace owner
to write and install new verbs gives him/her an easy way to circumvent
(caller == this) permissions checks.  Thus with verbs, it should still
be up to the object owner to decide whether to install something, and
the verb owner to decide what the code should be; the namespace owner
only gets to approve/forbid the use of a particular verbname s/he
controls.

GENERAL
=======
Allow the reservation of verb/property prefixes.  (N.B. the following
don't need to be builtins; with suitable wrapping on
set_property/verb_info, add/delete_property, add_verb, this can be
implemented in-DB in 1.8.0)

(1)  claim_verb_prefix(OBJECT, PREFIX, NAME_OWNER)

claim_verb_prefix reserves, on all descendants of OBJECT, the use of all
verbnames beginning with PREFIX for NAME_OWNER.  OBJECT can be #-1,
indicating that PREFIX is to be claimed for *all* objects.

Whenever a NAME is added to a verb on OBJECT1, either by add_verb() or
set_verb_info(), and NAME is reserved (go to the parent(OBJECT1), find
the longest prefix of NAME that is reserved --- if no prefix is
reserved, go to the grandparent,... if #-1 has no reservations, we're
done.  A variant of $generic_db can even implement this prefix lookup
rather quickly), find the corresponding NAME_OWNER of the reservation
and call NAME_OWNER:[name]accept_verb_name(OBJECT1, NAME); the name
assignment is allowed only if this returns true.

Any set_verb_info() that only affects names controlled by the caller may
be done even if the caller is not the owner of the object/verb in
question.

The caller of claim_verb_prefix() must either be wizardly or own OBJECT.
 In addition, if OBJECT:PREFIX is already reserved by some
PRIOR_NAME_OWNER, then
PRIOR_NAME_OWNER:[name]accept_verb_claim(OBJECT,PREFIX,NAME_OWNER) must
return true in order for the new reservation to go through.  

To summarize, if you claim a prefix, 

  :[name]accept_verb_name
     controls who gets to add verb names with that prefix, and

  :[name]accept_verb_claim
     controls who gets to
       claim extensions of your prefix or
       control of your prefix on specific descendants.

(2)  unclaim_verb_prefix(OBJECT, PREFIX)

undoes a prior claim_verb_prefix() and can be done by anyone who
(transitively) controls OBJECT:PREFIX.  Fails if OBJECT:PREFIX wasn't an
actual prior claim (e.g., if I claim #1:FRIB, I have to undo it with
unclaim #1:FRIB; unclaiming #1:FRIBBLE doesn't make sense unless I had
in fact allowed someone to previously claim #1:FRIBBLE and I want to
revoke that subclaim).

(3)  claim_property_prefix(OBJECT, PREFIX, NAME_OWNER)
(4)  unclaim_property_prefix(OBJECT, PREFIX)
     
would work likewise for properties.

nonwizard permissions checks for the various builtins:
  add_verb()
    callerperms needs write permission on object,
    ability to create verbs with specified owner
    AND any reserved names must be approved.
  set_verb_info()
    callerperms must be verb owner
    or the change is only in verbnames reserved by callerperms;
    all new reserved verbnames must be approved by name owners
  add_property
    name owner must approve (default for #-1.[A-Z], usual checks)
  delete_property
    name owner must approve (default for #-1.[A-Z], usual checks)
  set_property_info
    new flags must be approved by name owner.
    (I'd just as soon dispense with +c properties,
     they're almost entirely useless) 

For the sake of sanity, you'll probably want to have the prefixes for
the usual range of verb/property-names (i.e., "A","B",...,"Z") benignly
claimed on #-1 by a name owner that allows all name assignments but
forbids all subclaims.  This way, everyone will have a realm of
verbnames to use that is free from meddling by nutcase namespace owners.

For the interface scheme above, you'll need claims on #-1:"__0",
#-1:"__1", #-1:"__2", ... that dole out subclaims on a
first-come-first-served basis.




Home | Subject Index | Thread Index