Scoping Details#
Structures#
Halerium structures are built out of graphs, entities and variables. The building blocks can be nested to build deep and hierarchichal structures in a convenient way with the scoping mechanism.
Scoping#
Scope#
A scope is the context in a halerium structure. Scopes are managed via entering and exiting python with blocks.
Example:
a = Graph("a")
b = Graph("b")
# here the current scope is `noScope`
with a:
# here the current scope is `a`
pass
with b:
# here the current scope is `b`
pass
Scopetor#
A scopetor is an object that can provide such a scope.
The halerium scopetor classes are
Graph
,
Entity
,
Variable
,
and StaticVariable
.
So in the above example a
and b
could have been instances of any of
these classes.
Additionally, every Graph
instance comes with
an instance of Inputs
and
Outputs
. These are scopetors as well
g = Graph("g")
with g:
with g.inputs:
# the current scope is `g.inputs`
pass
with g.outputs:
# the current scope is `g.outputs`
pass
Scopee and child#
A scopee is an object that lives in a scope.
>>> g = Graph("g")
>>> with s:
>>> e = Entity("e") # `e` now lives in `g`. So the scope of `e` is `g`
>>> print(e.scope is g)
True
All scopetors can also scopees as well as all operators. If a scopee is also a scopetor it is referred to as a child of its scope.
>>> g = Graph("g")
>>> with s:
>>> e = Entity("e") # `e` is now a child of `g`.
>>> print(e in g.children.values())
True
All children of a scopee can be accessed as properties of the scopetor by their name.
g = Graph("g")
with g:
e = Entity("e")
with e:
v = Variable("v")
>>> g.e.v is v
True
Graphs start with two children, inputs
and outputs
>>> g = Graph("g")
>>> print(g.inputs.name)
inputs
>>> print(g.outputs.name)
outputs
Allowed combinations#
Not all scopetor/scopee combinations are allowed. There is a hierarchy. Graphs can contain everything, entities can contain everything except graphs and variables can only contain variables and operators. Inputs and outputs can only contain entities.
Scopetor |
Scopee |
|||
Graph |
Entity |
Variable |
Operator |
|
Graph |
✓ |
✓ |
✓ |
✓ |
Entity |
✗ |
✓ |
✓ |
✓ |
Variable/StaticVariable |
✗ |
✗ |
✓ |
✓ |
Inputs/Outputs |
✗ |
✓ |
✓ |
✗ |
Global and relative names#
The global name of a Scopee shows the full hierarchy of scope names separated by ‘/’ characters.
>>> g = Graph("g")
>>> with g:
>>> e = Entity("e")
>>> with e:
>>> v = Variable("v")
>>> with v:
>>> w = StaticVariable("w")
>>> print(w.global_name)
g/e/v/w
The relative name is the difference between the global name of a (grand-)parent and a (grand-)child.
>>> e.get_get_relative_name("g/e/v/w")
v/w
To fo from global or relative name to object use the get_child_by_name
method of scopetors.
>>> e.get_child_by_name("g/e/v/w", name_is_relative=False) is w
True
>>> g.get_child_by_name("v/w", name_is_relative=True) is w
True
Namespace convenience#
Halerium automatically puts references to children of a scopetor into the python namespace when the scope is entered and removes them if the scope is left.
g = Graph("g")
with g:
Entity("e")
>>> with g:
>>> print(e)
<HALerium.Entity at ...: name='e', global_name='g/e'>
>>> print(e)
NameError: name 'e' is not defined
This mechanism is applied as soon as a scopee is created.
>>> g = Graph("g")
>>> with g:
>>> Entity("e")
>>> print(e)
<HALerium.Entity at ...: name='e', global_name='g/e'>
Sibling references are removed when a deeper scope is entered
>>> g = Graph("g")
>>> with g:
>>> Entity("e")
>>> Entity("d")
>>> with d:
>>> print(g) # exists
>>> print(d) # exists
>>> print(e) # was removed when `d` was entered
<HALerium.Graph at ...: name='g', global_name='g'>
<HALerium.Entity at ...: name='d', global_name='g/d'>
NameError: name 'e' is not defined
and restored when the deeper scope is left.
>>> g = Graph("g")
>>> with g:
>>> Entity("e")
>>> Entity("d")
>>> with d:
>>> # print(e) # would cause a NameError
>>> print(e) # works
<HALerium.Entity at ...: name='e', global_name='g/e'>
Within a scope the references will trump user assigned variables,
>>> g = Graph("g")
>>> with g:
>>> e = 1.
>>> Entity("e")
>>> print(e)
<HALerium.Entity at ...: name='e', global_name='g/e'>
but as soon as the scope is left all user assigned variables are restored.
>>> g = Graph("g")
>>> with g:
>>> e = 1.
>>> Entity("e")
>>> print(e)
1.0
Remarks about namespace references#
Note that the references that halerium places into the namespace are
not the actual objects, but special references to them.
These references redirect all function calls etc.
to the actual object.
As a consequence the python built-ins is
and type
will distinguish
between the actual object and the reference.
>>> g = Graph("g")
>>> with g:
>>> Entity("e")
>>> print(g.e is e)
False
>>> print(type(g.e))
<class 'HALerium.entity.entity.Entity'>
>>> with g:
>>> print(type(e))
<class 'HALerium.scope.reference.EntityReference'>
For all other purposes the reference and the actual object are the same thing.