Root-relative namespace keys

Allison Randal allison at parrot.org
Wed Jan 7 00:44:47 UTC 2009


Patrick R. Michaud wrote:
> Today on #parrotsketch there was a lively discussion about handling
> root-relative classnames.  I volunteered to open a discussion with
> a summary of the problem, so here it is.
> 
> There are a number of places in PIR where we refer to class objects
> using a constant key.  Some examples:
> 
>     $P0 = newclass ['Foo';'Bar']
>     $P1 = subclass ['Hash'], ['MyHash']
>     $P2 = get_class ['MyHash']
>     $I0 = isa $P1, ['Hash']
>     .sub 'xyz' :multi(['MyHash'])
> 
> In each of the above, the key is treated as being a class in the
> current hll.  I think we all agree that this is as it should be.

Agreed.

> However, there are occasions where it's necessary to refer to a class
> in a different hll altogether.  We don't have a convenient way of
> supporting this.  Up to now, the canonical answer for this situation 
> has been for the PIR programmer to first obtain the namespace 
> corresponding to the class in the foreign HLL and use that as a 
> proxy for the class in the above.  There are at least two issues 
> with this approach: (1) it won't work for the :multi flag, and 
> (2) if the foreign namespace doesn't already exist, it has to 
> be created prior to doing any of the above operations, or otherwise
> special-case handled in PIR.

A namespaces should never be created for 'isa', 'get_class', or :multi. 
Only 'newclass' and 'subclass' create new namespaces.

> To illustrate #2:  Here's what is currently required to create 
> a ['Foo';'Bar'] class in a foreign hll:
> 
>     $P0 = get_root_namespace ['hll']
>     $P1 = split '::', 'Foo::Bar'
>     $P2 = $P0.'make_namespace'( $P0 )
>     $P3 = newclass $P2

Agreed, this could be made simpler. (There are multiple different ways 
to go about making it simpler.)

> Similarly, to check if an object isa ['Foo';'Bar'] from that other
> hll (and we don't already know that the ['Foo';'Bar'] class exists):
> 
>     $P99 = get_root_namespace ['hll';'Foo';'Bar']
>     if null $P99 goto not_isa
>     $I0 = isa $P0, $P99
>     goto done
>   not_isa:
>     $I0 = 0
>   done:

If the other hll namespace doesn't exist, then we can guarantee that the 
given object isn't from that class. That is, passing in a null PMC to 
'isa' should always return false. (Checking...) Okay, yes, a null PMC 
returns false for 'isa' so you could just do:

   $P99 = get_root_namespace ['hll';'Foo';'Bar']
   $I0 = isa $P0, $P99

> At the moment there are three options for resolving this:
> 
> A.  Require the hll as the first component of all keys referring
>     to classes.  (While this makes things very consistent, it also
>     de-huffmanizes class constants away from the far more common case 
>     where we're referring to classes in the same HLL, as well as
>     invalidating nearly all of our existing PIR code using classes,
>     so I'm rejecting it here.)

Agreed, this option is worse than what we currently have.

> B.  Since we seem to already have (ugly, expensive, and non-identical)
>     ways to handle foreign classes for everything except :multi above,
>     add some syntax to :multi that indicates whether a given key
>     is for the current hll or a foreign hll.
> 
> C.  Adopt a special form of key that indicates a class from a
>     foreign HLL.

Good summary of the alternatives we considered in the IRC discussion. 
Thanks.

> Personally I find option "C" to be far superior to the other alternatives.
> It works consistently everywhere and is far more efficient.
> 
> On a pragmatic level, we are currently limited in the ways that 
> we can augment a Key to indicate a class in a foreign hll.  
> Specialized string values in the key run a risk of colliding 
> with a valid namespace in a HLL, which would have to be guarded
> against.
> 
> Since string values in the key are potentially an issue, I
> find myself very drawn to Andrew's suggestion of simply using an
> integer 0 at the beginning of the key to indicate the key is for
> a foreign HLL.  This means that we would have:
> 
>     $P0 = newclass [0;'hll';'Foo';'Bar']
>     $P1 = subclass [0;'parrot';'Hash'], ['MyClass']
>     $P2 = get_class [0;'parrot';'PAST';'Block']
>     $I0 = isa $P1, [0;'parrot';'Hash']
>     .sub 'xyz' :multi([0;'parrot';'Integer'])
> 
> If using a special 0 constant in this way is too annoying, then
> perhaps the PIR compilers can be adjusted to have a syntax that
> is then converted to internally generate the above form of key:
> 
>     $P0 = get_class [ROOT;'parrot';'PAST';'Block']
>     #  Key generated as [0;'parrot';'PAST';'Block'] in bytecode
> 
> Other possibilities might be:
> 
>     [ROOT:'parrot';'PAST';'Block']
>     [hll:'parrot';'PAST';'Block']
> 
> The elegance of this solution seems to me to make it far superior
> to anything along the lines of option B.

Also a good summary of your position. This matches what I understood 
from the discussion.

I said I'd summarize some of the fundamental design principles involved 
in this proposal:

- Magical constants are bad. Replacing magical constants is one of the 
first principles of refactoring. (The various candidates like 0, null, 
'root', '*ROOT*' each have their own surface-level problems too: 
meaninglessness, obtuseness, possible conflicts with real HLL names, 
etc., but those are only secondary to the real problem.)

- Altering the fundamental behavior of core constructs as a quick fix 
for an unrelated problem is a "code smell". (This is for the proposed 
options that change the way key constants are parsed.) Code smells 
aren't an automatic "rejected", but they do count as a strike against 
the proposed solution, which means it has to be *substantially* superior 
to the alternatives before it's considered.

- Using a single construct with minor and non-obvious variations to do 
two completely different things is bad design. It increases the 
complexity of code maintenance because the developer must always 
consider all the things that construct could represent, instead of the 
one clear unambiguous meaning. Again, not an automatic rejection, but a 
strike against proposed solutions hampered by it.

- The HLL name and the class name should never be conflated, the HLL 
name is in no way part of the class name. (The current implementation 
does put HLL as just another level in the namespace tree. That's the 
cause of a lot of the pain we feel in the current HLL implementation.) 
HLL's each have their own separate namespace. The details of how an 
HLL's namespace is stored should be completely invisible to the 
developer. (Those details are also subject to change, especially with 
the security sandboxing additions.) It's very likely that the 'root' 
namespace won't even exist in 1.5, you'll just have a collection of HLL 
namespaces.

I'll propose alternatives in a separate email.

Allison


More information about the parrot-dev mailing list