After many months and lots of work, I'm happy to present you with the latest namespace spec draft. Comments are most welcome: to quote Chip, "The rest of the discussion would benefit from more eyes."
Thanks, -- matt diephouse http://matt.diephouse.com Synopsis - Languages should contain their namespaces - Namespaces should be hierarchical - Add a get_namespace opcode (that takes an array or a multidimensional hash index) - Namespaces follow the semantics of the HLL in which they're defined - Imports follow the semantics of the library's language - Two interfaces: typed and generic Namespace PMC API There are many different ways to implement a namespace and Parrot's target languages display a wide variety of them. By implementing an API, it should be possible to allow interoperability while still allowing each one choose the best internal representation. Definitions HLL A High Level Language, such as Perl, Python, or Tcl. This is in contrast to PIR. Current namespace The current namespace is the namespace associated with the currently executing subroutine. Naming Conventions For interoperability, languages should use hierarchical namespaces. There's no way to outlaw flat namespaces, of course, and that remains an option for language implementors, but a standard must be chosen for easy interoperability, and hierarchical seems to be the better choice. HLL Private Namespaces HLLs should use a namespace with an underscore and the lowercased name of the HLL to store any private items. For instance, Tcl should use the "_tcl" namespace to store the subroutines that make up the compiler. User Defined Namespaces All HLLs should prefix any namespaces with the lowercased name of the HLL (so there's no question of what to capitalize). So Perl 5's CGI module should be stored under perl5::CGI. This eliminates any accidental collisions between languages. Interfaces: Generic and Typed Most languages leave their symbols plain, which makes lookups quite straightforward. Others use sigils or other mangling techniques, complicating the problem of interoperability. Parrot namespaces assist with interoperability by providing two interface subsets: the *raw interface* and the *typed interface*. Raw Interface Each HLL may, when working with its own namespace objects, use the *raw interface*, which allows direct naming in the native style of the namespace's HLL. This interface consists of the following methods: TODO: Decide whether this is best exposed by "does hash". add($S0, $P0) Store the PMC in $P0 under the raw name $S0. del($S0) Delete the entry named $S0. $P0 = find($S0) Find the sub, namespace, or variable named $S0. Return a null PMC on lookup failure. $P0 = names() Get an array of the names contained in the namespace. NOTE: The return value may be a psuedo-array holding a reference to the namespace and some kind of internal iterator. Typed Interface When a given namespace's HLL is either different from the current HLL or unknown, an HLL should generally use only the language-agnostic namespace interface. This interface isolates HLLs from each others' naming quirks. It consists of add_foo(), find_foo(), and del_foo() methods, for values of "foo" including "sub" (something executable), "namespace" (something in which to find more names), and "var" (anything). NOTE: The job of the typed interface is to bridge *naming* differences, and *only* naming differences. It does not enforce, nor even notice, the interface requirements of "sub" or "namespace". Thus, for example, successful execution of add_sub("foo", $P0) does *not* automatically guarantee that $P0 is an invokable subroutine. Caveat usor. add_sub($S0, $P0) Store $P0 as a subroutine with the name of $S0. add_namespace($S0, $P0) Store $P0 as a sub-namespace with the name of $S0. Note that to add Bar::Baz to the Foo namespace, one must add Bar to Foo and then Baz to Foo::Bar. add_var($S0, $P0) Store $P0 as a variable under the name of $S0. IMPLEMENTATION NOTE: perl6::namespace.add_var may choose to check which parts of the variable interface are implemented by $P0 so it can decide on an appropriate sigil. del_sub($S0) del_namespace($S0) del_var($S0) Delete the sub, namespace, or variable named $S0. $P0 = find_sub($S0) $P0 = find_namespace($S0) $P0 = find_var($S0) Find the sub, namespace, or variable named $S0. IMPLEMENTATION NOTE: perl6::namespace.find_var should check all variable sigils, but the order is not to be counted on by users. If you're planning to let Python code see your module, don't have both "our $A" and "our @A". $P0 = subs() $P0 = namespaces() $P0 = vars() Get an array of the subs, namespaces, or variables in the namespace. import_into($P0, ...) Import items from the current namespace into the namespace in $P0. The namespace may choose to take any number of arguments for the import routine, such as the name of subroutines, patterns, or tags. These always follow the conventions of the callee and not the caller. This aligns with the behavior that users will expect, so that `use tcl:Some::Module 'c*'` will DTRT and import all the commands that start with 'c' from the Tcl namespace into the Perl namespace. IMPLEMENTATION NOTE: Most namespace import_into implementations will restrict themselves to using the typed interface on the target namespace. However, they may also decide to check the type of the target namespace and, if it turns out to be of a compatible type, to use same-language shortcuts. store_var($S0, $P0) Store the variable $P0 under the name $S0. Non-interface Methods These methods don't belong to either the typed or the generic interface. $P0 = name() Returns the name of the namespace as an array of strings. So perl5:Some::Module would return an array containing "Some", "Module". NOTE: This is a naive method. It does not account for any aliasing. Class PMC API Class should inherit from the Namespace PMC. Methods add_method($S0, $P0) Add a method $P0 under the name $S0. del_method($S0) Delete the method named $S0. $P0 = find_method($S0) Find the method named $S0. $P0 = methods() Return an array of the methods in the class. Compiler PMC API Methods load_library($S0, $S1, ...) Ask this compiler to load a library/module named by the strings $S0, $S1, ... So perl5:Some::Module should be loaded using "perl5.load_library("Some", "Module")". Subroutine PMC API Some information must be available about subroutines to implement the correct behavior about namespaces. Data get_namespace The namespace where the subroutine was defined. (As opposed to a namespace that it was imported into.) Namespace Opcodes add_namespace $P0, $P1 Add the namespace PMC $P1 as the namespace $P0 (an array of names or a multidimensional hash index). del_namespace $P0 Delete the namespace $P0 (an array of names or a multidimensional hash index). $P0 = find_global $P1, $S0 $P0 = find_global $S0 Find the variable $S0 in $P1 or the current namespace. $P0 = get_namespace $P1 $P0 = get_namespace Get the namespace $P1 (an array of names or a multidimensional hash index) or the current namespace. To get the "Foo::Bar" namespace, one would use this: $P0 = split "::", "Foo::Bar" $P1 = get_namespace $P0 or this: $P1 = get_namespace ["Foo"; "Bar"] store_global $P1, $S0, $P0 store_global $S0, $P0 Store $P0 as the variable $S0 in $P1 or the current namespace. HLL Namespace Mapping In order to make this work, Parrot must somehow figure out what type of namespace pmc to create. Default Namespace The default namespace PMC will implement Parrot's current behavior. Compile-time Creation This perl: #!/usr/bin/perl package Foo; $x = 5; should map roughly to this PIR: .HLL "Perl5", "perl5_group" .namespace [ "Foo" ] .sub main :main $P0 = new .PerlInt $P0 = 5 store_global "$x", $P0 .end In this case, the "main" sub would be tied to Perl5 by the ".HLL" directive, so a Perl5 namespace would be created. Run-time Creation Consider the following Perl5 program: #!/usr/bin/perl $a = 'x'; ${"Foo::$a"} = 5; The Foo:: namespace is created at run-time (without any optimizations). In these cases, Parrot should create the namespace based on the HLL of the PIR subroutine that calls the store function. .HLL "Perl5", "perl5_group" .sub main :main # $a = 'x'; $P0 = new .PerlString $P0 = "x" store_global "$a", a # ${"Foo::$a"} = 5; $P1 = new PerlString $P1 = "Foo::" $P1 .= $P0 $S0 = $P1 $P2 = split "::", $S0 $S0 = pop $P2 $S0 = "$" . $S0 $P3 = new .PerlInt $P3 = 5 store_global $P2, $S0, $P3 .end In this case, "store_global" should see that it was called from "main", which is in a Perl5 namespace, so "Foo::" should be also created as a Perl 5 namespace. Language Notes Perl 6 Sigils Perl6 may wish to be able to access the namespace as a hash with sigils. That is certainly possible, even with subroutines and methods. It's not important that a HLL use the typed namespace API, it is only important that it provides it for others to use. So Perl6 may implement get_keyed and set_keyed VTABLE slots that allow the namespace PMC to be used as a hash. The "find_sub" and "find_method" methods would, in this case, would append a "&" sigil to the front of the sub/method name and search in the internal hash. Submethods Submethods should be handled by adding an additional Class method "add_submethod" and adding another internal hash (submethods can have the same name as methods, I believe). Python Subroutines and Namespaces Since Python's subroutines and namespaces are just variables (the namespace collides there), the Python PMC's "find_var" method may return subroutines as variables. Examples Aliasing Perl: #!/usr/bin/perl6 sub foo {...} %Foo::{"&bar"} = &foo; PIR: .sub main :main $P0 = find_name "&foo" $P1 = get_namespace ["perl6"; "Foo"] # A smart perl6 compiler would emit this, # because it knows that Foo is a perl6 namespace: # $P1["&bar"] = $P0 # But a naive one would emit this: $P1.add_sub("bar", $P0) end .end .sub foo ... .end Cross-language Importing Perl: #!/usr/bin/perl use tcl:Some::Module 'w*'; write("this is a tcl command"); PIR: .sub main :main .local pmc tcl tcl = compreg "tcl" tcl.load_library("Some", "Module") $P0 = get_namespace $P1 = get_namespace ["tcl"; "Some"; "Module"] add_namespace ["perl5"; "Some"; "Module"], $P1 $P1.import_into($P0, 'w*') write("this is a tcl command") end .end