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

Reply via email to