apldyalog

Can someone explain this behavior for finding parent/child relationships of unnamed Namespaces?


I'm trying to use ## to find the parent of a namespace, and then ⎕NL -9 to find its children.

Suppose I have a parent namespace: 'parent' ⎕NS ''

Now I have 2 child namespaces: One named, and one unnamed:

'namedChild' parent.⎕NS ''

unnamedChild ← parent.⎕NS ''

When I do:

parent.⎕NL -9 

why do I not see the unnamed child?

Where exactly does the namespace exist?


Solution

  • It is important to distinguish between the name which refers to a namespace – the "ref" – and "the namespace itself":

          )cs #
          'parent' ⎕NS ''
          unnamedChild ← parent.⎕NS ''
    

    This creates a namespace which has "parent" as its parent, AND a reference to that namespace called #.unnamedChild. ⎕NL only deals in names ("Name List"), it has no idea where the thing that the ref points to is.

          #.⎕nl -9
    parent  unnamedChild
    

    Creating a reference to a namespace does not change its parent. In fact, we can have many references to the same namespace:

          ns2←⎕NS ''
          ns2.reftoChild←#.unnamedChild
          ns2.reftoChild.##
    #.parent
    

    The parent is entirely decided by the space where ⎕NS '' was run, in the above case that is "parent", or in the case of a dyadic ⎕NS, the parent name is given explicitly in the left argument. When you )CS into parent and create unnamedChild there, this is the equivalent of:

          parent.(unnamedChild ← ⎕NS '')
    

    And note, the "exact opposite" of

          parent.unnamedChild←⎕NS''
          parent.unnamedChild
    #.[Namespace]
          parent.unnamedChild.##
    #
    

    This creates a named ref in parent, but to an unnamed namespace parented by #.

    It is very important to manage "both ends of the ref", in other words where you create the name and run the ⎕NS, using parentheses like those above. Note that even if we now erase #.parent, #.unnamedChild has a reference to it which will keep it alive, this is a frequent cause of "lost space" in a workspace, and resulting bad performance:

          ⎕EX 'parent'
          #.⎕nl -9
    ns2  unnamedChild 
          unnamedChild.##
    #.parent
    

    But that name no longer exists, I hear you say! That is correct, but there is an "external reference" to an anonymous namespace within it, and the parent pointer from this keeps it alive. Every namespace is stamped with its own name at creation and remembers it, regardless of whether the name containing the parent ref still exists. So if you can reach it, it will tell you the name it was born with.

    Even the so-called "unnamed" namespace is given a name which tells you where it was created:

          unnamedChild
    #.parent.[Namespace]
    
         'new' ⎕NS ''
          old←new
          ⎕EX 'new'
          old
    #.new
    

    You can "fix" this:

          old.⎕DF '#.old' ⍝ Set the display form
          old
    #.old
    

    Some of our tools use this for documentation, for example ⎕SE.Link.Links is an array of namespaces containing information about active links, which we name so that they tell you which link they represent:

          ]link.create linkdemo c:\tmp\linkdemo
    Linked: #.linkdemo ←→ c:\tmp\linkdemo
          ⎕SE.Link.Links[1] ⍝ a namespace describing the link we just created
    [⎕SE.Link ←→ C:\devt\link\StartupSession\Link]
    

    The display form doesn't have to be a vector:

          old.⎕DF ↑'ekki' 'ekki' 'ekki' 'ptang' 'zoom-boing' '(previously known as "ni!")'
          old
    ekki                       
    ekki
    ekki                       
    ptang                      
    zoom-boing                      
    (previously known as "ni!")
    

    However, note that the "original name" is still remembered when naming children:

          old.⎕NS ''
    #.new.[Namespace]