I, like probably many, was very confused and intimidated by NameSpace management for a long time. I recently started managing sub-variable integrity manually, and the relevance of namespaces finally became clear.
Instead of passing variables %0-%n to a static %sub_variable, and then deleting them at the exit of the sub, EUO already has tools to manage this for you. By declaring a local namespace for the sub and clearing it and recalling the previous namespace, all that legwork is handled for you (for the majority of scenarios).
My question has to do with push/pop and I'm looking for confirmation or correction on what I understand to be limitations. I am not an expert, but I believe I have the logic of namespace worked out.
*FYI*: For the context of this thread I will assume that any Sub will populate a sub-local namespace, and as such I will purposefully conflate "namespace," with "sub." I want to frame namespace management in the context of sub-local variable management so that namespaces novices like myself can more easily interpret a namespace use case.
This will likely be most people's first experience/need for NameSpace Management. The NameSpace function has more usages than sub-variable management, but grasping this specific usage will translate to understanding when other uses are necessary.
*Properly defining "many," without making assumptions (every sub will populate a sub-local namespace) quickly degrades into a recursive argument nightmare.
For those unfamiliar with NameSpace Push and Pop:
;---------------------------------------------------------------
NameSpace Push saves the current namespace as a temporary variable. Probably something like (pseudo-code) #pushed_nsname|type. All of a namespace's defined child !variables are stored within their parent namespace address, regardless of whether it was "pushed," or not.
NameSpace Pop sets the name space to the
LAST "pushed," namespace
AND can only be called once per push/pop (EUO seems to clear #pushed_nsname|type when pop is called).
If NameSpace Push is called multiple times before the cursor parses a NameSpace Pop, EUO will overwrite #pushed_nsname|type every time, destroying NameSpace Pop integrity.
As long as a sub maintains a 1:1 relationship, as in the called sub will only be called directly and does not call other subs, Push/Pop works fine.
Example of 1:1:
;current namespace = default namespace = local std
body:
...
gosub one_to_one
...
goto body
sub one_to_one ;Sub One_To_One will only ever be called directly and never calls other subs.
namespace push ;Saves the current (local std) namespace, including
; any defined !variables for recall with pop.
namespace local one_to_one ;Changes the current (local std) namespace to "local one_to_one."
;Sub One_To_One Body.
;Do Sub One_To_One things setting local !variables and referencing local !variables
; as well as setting script %variables to be passed to the script,
; and referencing script %variables previously defined.
...
namespace clear ;Deletes all !variables for the current (local one_to_one) namespace.
namespace pop ;Changes the current (local one_to_one) namespace
; back to the last_pushed (local std) namespace
; and restores the stored !variables for it.
return
But as soon as any directly called sub calls an/other sub(s), or a sub can be called directly and/or by an/other sub(s), or both/all (1:many || many:1 || many:many), push/pop integrity breaks down, and namespace integrity must be maintained manually. Luckily EUO also has #system_variables to handle this: #nsname and #nstype.
*Note: There is more nuance to defining "many," but in the context of this thread,
where I assume that all subs will populate a sub-local namespace,
it is safe to assume my definitions above are correct.
My solution for this conundrum, for when I develop a new subroutine, is to assume that all subs will populate a sub-local namespace and have a many:many call relationship and manually store and delete #nsname and #nstype, and edit this out if I want to. Conversely I could assume this is not the case and add this in after the fact, but that doesn't change the solution.
Example solution for all relationship states (1:1 || 1:many || many:1 || many:many):
sub many_to_many
set %many_to_many_nsname #nsname ;store the current #nsname and #nsntype
set %many_to_many_nstype #nstype ;before populating another namespace
namespace local many_to_many ;or whatever the sub is named
;Do sub many_to_many things
...
namespace clear
namespace %many_to_many_nstype %many_to_many_nsname
deletevar %many_to_many_nsname
deletevar %many_to_many_nstype
return
This solution works the same as push/pop, except with several advantages:
1. You don't have to know the final state of a sub's namespace relationship (n:n).
2. It frees up push/pop for specified use elsewhere in your script/subs, if you prefer.
3. You may ignore the current state of push/pop in publicly published subs.
4. Each current namespace is permanently stored until all calls are returned.
The only disadvantage I can think of is that it costs 3 more lines/parse processes per potential push/pop, but that is immediately nullified when a potential push/pop push is overwritten.
Back to my original question, am I over-thinking this or is this a relevant solution?
In simpler terms, "Am I doing this right?" Or is there a better way?
Thanks for your time.