Creating Parrot classes currently requires _typed_ namespaces, that is,
namespaces where more than one object can have the same fully-qualified
name. Parrot's default namespace allows that there be both a namespace and
another object (often a class) with the same name.
Namespace typing is a fine feature for any given HLL to use, but a basic
function of Parrot like class creation must not depend on it.
There's also a more subtle flaw: a violation of the principle of least
repetition. Given a class (let's say it's a Perl 6 class) named foo::bar,
two distinct Parrot entities, both named foo;bar, must be created to
implement it: the class object, and its method namespace. This is weak
design. For example, it makes removing (or renaming) a class into a
multi-step operation.
So here are the changes I'm planning to PDD today:
(1) By default, the implementation objects of a class foo;bar will consist of:
a namespace named foo;bar
a class object named foo;bar;__parrot_class
(2) HLLs will be asked to never use names matching /^__parrot/ for any
purpose.
This edict would apply to HLL maintainers only. (None of us can be held
responsible for the actions of HLL users.) It would also be a more general
rule that we could use to drop other Parrot implementation objects (e.g.
thunks or things we might need to make MMD work) into user-controlled
namespaces.
(3) Parrot default namespaces will be entirely untyped.
That is, in a Parrot default namespace, there will only ever be a maximum of
one object with a given fully qualified name.
==[ Entering alternative universe ]===========================
An alternativeq design has the _classes_ being the visible objects, with
namespaces being externally anonymous and only visible by indirecting
through the classes that use them. This alternative is, of course, entirely
feasible, but it has downsides.
First, some namespaces would become more equal than others: class
namespaces, vs. namespaces unrelated to classes, would require a new
technique for lookup, or else some convention that classes would provide a
namespace interface; neither of these options appeals, especially since the
base namespace interface is 'hash', and I doubt that classes responding to
the hash interface meets anybody's idea of "elegant".
Second (and much less worrisome), users would have to create class objects
before defining any methods for them.
==[ Normality restored ]======================================
==[ Anything you can't handle is now your own problem ]=======
--
Chip Salzenberg <ch...@pobox.com>
I don't like the impact that this is going to have on HLL languages -
it's exposing internal bits of parrot to the HLL programmers. The
argument that we already expose some bits (for example, certain
method names are restricted for classes), does not, in my mind, mean
it's OK to add more. We should be removing the bits that already are
exposed. (see also ancient python discussions.)
My concern is that, in an effort to work around this restriction,
each language will end up implementing a workaround to hide the
__parrot* namespaces: these workarounds will hinder interoperability
unless they are synchronized: if they're synchronized, why not
canonicalize them and put it in the core?
That said, I'm still trying to figure out why a class's methods have
to be stored in a namespace: I don't see any reference to this in the
object or namespace PDDs, except for a single line in PDD15:
Classes have an associated namespace. (Which may be anonymous)
Can you provide some insight here? Matt Diephouse suggests that it's
because methods are subs, and subs live in a namespace - but is there
a use case (HLL requirement) for doing _namespace_ lookup for finding
a method in a class? That is, is anyone ever going to call methods
by looking them up in a namespace PMC and invoking them, as opposed to:
$P1.'method'()
or
$P2 = find_method $P1, 'method'
$P2()
In both cases here, we're going off the vtable for that particular PMC.
Regards.
On Wed, May 17, 2006 at 12:18:27PM -0400, Will Coleda wrote:
> My concern is that, in an effort to work around this restriction,
> each language will end up implementing a workaround to hide the
> __parrot* namespaces: these workarounds will hinder interoperability
> unless they are synchronized: if they're synchronized, why not
> canonicalize them and put it in the core?
That's a good argument, and I take it seriously.
My initial answer is that you're aiming for a goal I don't think is worth
trying for. I simply don't expect the Parrot-hosted versions of HLLs to be
100% indistinguishable from their other versions from a user experience
point of view, in the same sense that various C compilers or Unix variants
are distinguishable to users, despite their efforts at common support.
Since a 100% invisible Parrot is not achievable, we can start considering
the practical consequences of any given visibility. I don't imagine that
more than a tiny fraction of _user_ code would be at all inconvenienced by
the appearance of /^__parrot/ names in their namespaces. (Or /^\0P\0/ e.g.)
OTOH, as you observe, HLL authors may well need to account for it, and two
parallel HLL hacks generally imply the need for a standard Parrot hack.
> That said, I'm still trying to figure out why a class's methods have
> to be stored in a namespace ...
Well, that arrangement isn't necessary per se. In fact, we already have a
real-world exception -- methods of the "namespace" class are not stored in a
namespace, for chicken-and-egg reasons. That said, there are still some
egg-and-chicken issues with defining methods without using a namespace.
(See below.)
> Matt Diephouse suggests that it's because methods are subs, and subs live
> in a namespace, but is there a use case (HLL requirement) for doing
> _namespace_ lookup for finding a method in a class?
The user for which the use case exists is no HLL, but Parrot itself:
Classes can be created at runtime ("newclass"), while method definitions (as
all subs) are acquired at pbc load time. When Parrot loads the methods for
a class, that class per se may not even exist yet! In the gap, mehtods must
be stored _somewhere_, and right now, namespaces serve that purpose.
Thus, removing the ".namespace ['classname'] ... .sub methodname" idiom for
runtime-created classes requires the invention of some new idiom, and it is
that invention that the "__parrot_class" proposal dodges.
OTOH, I could just invent the PIR syntax and let Leo figure out how to
implement it. Hey, it worked for the ISO C++ committee. :-/
--
Chip Salzenberg <ch...@pobox.com>
> There's is a problem with the naming of classes and their associated
> namespaces
> that must be addressed.
The first question is: why is the namespace associated, or - in other
words - why does a class C<has> a namespace instead of C<isa>
namespace. The latter is AFAIK true for Perl6.
See below[1] for more.
> Creating Parrot classes currently requires _typed_ namespaces, that is,
> namespaces where more than one object can have the same fully-qualified
> name. Parrot's default namespace allows that there be both a
> namespace and
> another object (often a class) with the same name.
>
> Namespace typing is a fine feature for any given HLL to use, but a
> basic
> function of Parrot like class creation must not depend on it.
Why? It's implemented and is working.
> There's also a more subtle flaw: a violation of the principle of least
> repetition. Given a class (let's say it's a Perl 6 class) named
> foo::bar,
> two distinct Parrot entities, both named foo;bar, must be created to
> implement it: the class object, and its method namespace. This is weak
> design. For example, it makes removing (or renaming) a class into a
> multi-step operation.
This would simple be solved with the class C<isa> namespace.
> So here are the changes I'm planning to PDD today:
>
> (1) By default, the implementation objects of a class foo;bar will
> consist of:
>
> a namespace named foo;bar
> a class object named foo;bar;__parrot_class
This isn't quite correct. The first line is actually:
a namespace named $HLL;foo;bar
which leads to another can of worms[2], IMHO. I don't like the
"__parrot_class" magic. A flag bit (PObj_is_class_FLAG) set in the
namespace PMC could achieve the same. But it's still requiring the
construction of 2 objects.
[2]
The implicit $HLL in all namespace operations seems to complicate all
tasks that cross namespace boundaries, e.g. the whole compiler tool
chain or parrot libs. I'd really prefer an explicit toplevel namespace,
which is under the control of the code generation.
> ==[ Entering alternative universe ]===========================
>
> An alternativeq design has the _classes_ being the visible objects,
> with
> namespaces being externally anonymous and only visible by indirecting
> through the classes that use them. This alternative is, of course,
> entirely
> feasible, but it has downsides.
This looks like the C<isa> approach.
> First, some namespaces would become more equal than others: class
> namespaces, vs. namespaces unrelated to classes, would require a new
> technique for lookup, or else some convention that classes would
> provide a
> namespace interface; neither of these options appeals, especially
> since the
> base namespace interface is 'hash', and I doubt that classes
> responding to
> the hash interface meets anybody's idea of "elegant".
[1] The interface problem.
A namespace needs a distinct interface - one that isn't used by any
class like 'Hash'. Or IOW - given the strong relationship between
namespaces and classes - the namespace interface is a part of the class
interface, which we actually already have (in some parts):
VTABLE methods:
find_method, add_method, remove_method
set_attr_str, get_attr_str
opcodes:
addattribute, removeattribute
The latter 2 should probably be virtualized via the interface too. The
'remove'-thingies are all unimplemented.
Now given that an attribute is just the general case[3], it would
correspond to the untyped namespace interface. The '*_method' interface
deals with subs in a namespace, which is one part of the typed
interface.
[3] e.g. for Python:
m = o.f # get_attribute, returning a 'bound' method
# a limited currying operation
There is of course sill the chicken and egg problem of namespaces, when
we want to sublass a namespace, which might need some vtable invocation
directly via the namespace vtable as opposed to find_method.
This all is also strongly related to interfaces, which we actually
don't have yet. A vtable is implementing all internally used interfaces
for all PMCs and classes. A vtable, split into distinct interface
slots, could be much more flexible, as each PMC could define it's own
(and only the needed) interface functions. E.g.
obj->vtable->scalar_if->abs() # scalar interface slots
->neg() ...
ns ->vtable->ns_if ->find_sub() # namespace interface slots
...
This would allow to customize methods per interface or per PMC/class
(when that's the only one with that specific interface).
leo
When I went to implement the rest, I found it was already mostly there:
default.pmc already implements the "add_method" vtable entry, but there's no
"addmethod" opcode... or at least, there _was_ no "addmethod" opcode until
a minute ago. :-) This example now works:
.sub main :main
.local pmc c
c = newclass ['whatever']
.const .Sub foo = "whatever_foo"
addmethod c, "foo", foo
$P0 = new ['whatever']
$P0.foo()
.end
.sub whatever_foo :anon :method
print "Foo!\n"
.end
Note that the default implementation of vtable add_method() still depends on
public namespaces. But we can fix that. }:-)
--
Chip Salzenberg <ch...@pobox.com>
1) (Can we|Will we be able to) do the whole newclass/addattribute/addmethod
thing at compile time (in a :immediate), so that you have your classes
defined and just freeze the class PMC, then thaw 'em and be done when you
run? Just thinking of the common case where we know a load of class details
at compile time (like in .Net ;-)).
2) What about class (aka static) methods?
Thanks,
Jonathan
Sometimes "dynamic" is remarkably convenient. :-)
> 1) (Can we|Will we be able to) do the whole newclass/addattribute/addmethod
> thing at compile time (in a :immediate), so that you have your classes
> defined and just freeze the class PMC, then thaw 'em and be done when you
> run?
It should work. If not, patches or tickets welcome.
Note, though, that the thawed PMC will vary from the frozen original in one
respect, by design: new type numbers must be assigned on each thaw. When
this happens, ParrotClass, and all other class-like PMCs, should be able to
cope. If any of the above doesn't happen, patches or tickets welcome.
> 2) What about class (aka static) methods?
At this point, IIRC, class and object methods all live in the same
namespace. Some HLLs are probably OK with that -- Perl 5 for example does
fine with this arrangement [not that Perl 5 will be creating ParrotClasses].
But obviously this is not a tenable situation long-term. In the short term
you may have to hack up a workaround, like mangling all class method names
or something.
--
Chip Salzenberg <ch...@pobox.com>