Lichen

Annotated docs/wiki/Structure

939:8a70350be7b0
2021-06-29 Paul Boddie Merged changes from the default branch. trailing-data
paul@810 1
= Program Structure =
paul@810 2
paul@861 3
A program consists of a number of '''modules''' with each module providing its
paul@861 4
own namespace. Modules can be organised within '''packages''' which define a
paul@861 5
hierarchical relationship between them. However, the relationship between
paul@861 6
modules is not automatically exposed within the program: it is more
paul@861 7
appropriate to consider modules as independent entities that have a particular
paul@861 8
naming scheme.
paul@810 9
paul@861 10
Within each module a hierarchy of namespaces is provided by '''classes''',
paul@861 11
with each class potentially containing other classes, and so on. Also
paul@861 12
appearing within modules and classes are '''functions''', with those appearing
paul@861 13
within classes being regarded as '''methods'''.
paul@810 14
paul@861 15
Function namespaces are considered separately from module and class namespaces
paul@861 16
and are not considered part of the general namespace hierarchy, instead merely
paul@861 17
appearing as objects at the edges of that hierarchy. Functions may also be
paul@861 18
defined within functions, and such inner functions will be referenced within
paul@861 19
their parent functions, but no hierarchical relationship will exist between
paul@861 20
their namespaces.
paul@810 21
paul@861 22
Thus, a program provides a static namespace hierarchy consisting of modules
paul@861 23
containing classes (containing other classes, and so on) plus functions.
paul@861 24
Objects residing within namespaces are accessed via '''attributes''' of those
paul@861 25
namespaces.
paul@810 26
paul@810 27
{{{{#!table
paul@810 28
{{{#!graphviz
paul@810 29
//format=svg
paul@810 30
//transform=notugly
paul@810 31
digraph program {
paul@911 32
  node [shape=box,fontsize="13.0",fontname="sans-serif",tooltip="Program structure"];
paul@810 33
  edge [tooltip="Program structure"];
paul@810 34
  rankdir=LR;
paul@810 35
paul@810 36
  program [label="program",shape=folder,style=filled,fillcolor=cyan];
paul@810 37
paul@810 38
  subgraph {
paul@810 39
    rank=same;
paul@810 40
    moduleM [label="module M",style=filled,fillcolor=gold];
paul@810 41
    moduleN [label="module N\n(package)",style=filled,fillcolor=gold];
paul@810 42
  }
paul@810 43
paul@810 44
  subgraph {
paul@810 45
    rank=same;
paul@810 46
    classA [label="class A"];
paul@810 47
    moduleNP [label="module N.P",style=filled,fillcolor=gold];
paul@810 48
  }
paul@810 49
paul@810 50
  subgraph {
paul@810 51
    rank=same;
paul@810 52
    functionF [label="function f",shape=ellipse];
paul@810 53
    functionG [label="function g",shape=ellipse];
paul@810 54
    classB [label="class B"];
paul@810 55
    classC [label="class C"];
paul@810 56
  }
paul@810 57
paul@810 58
  subgraph {
paul@810 59
    rank=same;
paul@810 60
    functionJ [label="function j",shape=ellipse];
paul@810 61
    functionH [label="function h",shape=ellipse];
paul@810 62
    functionK [label="function k",shape=ellipse];
paul@810 63
  }
paul@810 64
paul@810 65
  program -> moduleM;
paul@810 66
  program -> moduleN;
paul@810 67
paul@810 68
  moduleM -> classA;
paul@810 69
  moduleN -> moduleNP;
paul@810 70
  moduleNP -> classC;
paul@810 71
paul@810 72
  classA -> functionF;
paul@810 73
  classA -> functionG;
paul@810 74
  classA -> classB;
paul@810 75
  classB -> functionH;
paul@810 76
  classC -> functionK;
paul@810 77
paul@810 78
  functionG -> functionJ [style=dashed];
paul@810 79
}
paul@810 80
}}}
paul@810 81
||
paul@810 82
{{{#!python numbers=disable
paul@810 83
# module M
paul@810 84
paul@810 85
class A:
paul@810 86
    def f(...): pass
paul@810 87
paul@810 88
    def g(...):
paul@810 89
        def j(...): pass
paul@810 90
paul@810 91
    class B:
paul@810 92
        def h(...): pass
paul@810 93
paul@810 94
# module N
paul@810 95
paul@810 96
...
paul@810 97
paul@810 98
# module N.P
paul@810 99
paul@810 100
class C:
paul@810 101
    def k(...): pass
paul@810 102
}}}
paul@810 103
}}}}
paul@810 104
paul@810 105
== Referencing the Structure ==
paul@810 106
paul@861 107
Each part of the structure is catalogued using an '''object path''',
paul@861 108
indicating its location in the structure hierarchy, and a reference indicating
paul@861 109
its nature and origin. For example:
paul@810 110
paul@810 111
|| '''Object Path''' || '''Reference''' || '''Explanation''' ||
paul@810 112
|| `M.A` || `<class>:M.A` || The definition of class `A` in module `M` ||
paul@810 113
|| `O.A` || `<class>:M.A` || A reference to `M.A`, a class ||
paul@810 114
|| `N.P.C.k` || `<function>:N.P.C.k` || The definition of method `k` in class `C` of module `N.P` ||
paul@810 115
|| `O.k` || `<function>:N.P.C.k` || A reference to `N.P.C.k`, a function ||
paul@810 116
|| `O.values` || `<instance>:__builtins__.list.list` || An object identified as an instance of class `__builtins__.list.list` ||
paul@810 117
|| `__main__.M` || `<module>:M` || A reference to module `M` ||
paul@810 118
|| `__main__.counter` || `<var>` || An undetermined object called `counter` in the `__main__` module ||
paul@810 119
paul@861 120
The reference therefore expresses the '''kind''' of object (class, function,
paul@861 121
instance, module or undetermined variable), possibly accompanied by an object
paul@861 122
type, and also possibly accompanied by an alias indicating where the reference
paul@861 123
was obtained.
paul@810 124
paul@810 125
== Classes ==
paul@810 126
paul@861 127
Classes are regarded as statically-defined objects, meaning that they are only
paul@861 128
evaluated once and must not be defined within conditional sections or within
paul@861 129
functions. Base classes must be expressed using readily-identifiable class
paul@861 130
names, since any potential ambiguity or uncertainty with the identity of base
paul@861 131
classes could result in a class inheritance hierarchy that is effectively
paul@861 132
dynamic.
paul@810 133
paul@861 134
When '''instantiated''', classes provide '''instances''' that provide the
paul@861 135
attributes of each class's namespace plus any '''inherited''' attributes from
paul@861 136
base classes that would not be provided by the class itself. In addition,
paul@861 137
instances provide their own instance attributes.
paul@810 138
paul@810 139
== Functions ==
paul@810 140
paul@861 141
Functions are regarded as statically-defined objects, but they may be defined
paul@861 142
either with names within other named functions or as '''lambdas''' (functions
paul@861 143
without names) within named functions or other lambdas. Thus, unlike classes,
paul@861 144
functions may be defined once but replicated many times, each time with
paul@861 145
different accompanying state information. Such state information needs to be
paul@861 146
provided via default parameters: it is not detected and propagated
paul@861 147
automatically. Closures are not supported: any state from enclosing scopes
paul@861 148
must be supplied at the point of definition of a function; it is not acquired
paul@861 149
from outer functions by name.
paul@810 150
paul@810 151
{{{#!python numbers=disable
paul@810 152
def outer(a):
paul@810 153
    b = 2
paul@810 154
    def inner(c, a=a, b=b):
paul@810 155
        # a initialised using default from outer scope
paul@810 156
        # b also initialised using default from outer scope
paul@810 157
        return a, b, c
paul@810 158
    b = 4
paul@810 159
paul@810 160
outer(1)(3) # returns (1, 2, 3) not (1, 4, 3)
paul@810 161
}}}
paul@810 162
paul@810 163
=== Lambdas ===
paul@810 164
paul@861 165
Lambdas are given special names for the purposes of identification within the
paul@861 166
program structure, being named relative to the scope in which they are
paul@861 167
defined. For example, the first lambda appearing within module `N` would have
paul@861 168
an object path of `N.$l0`, and a subsequent lambda within the same scope would
paul@861 169
have an object path of `N.$l1`. Lambdas may appear within classes and
paul@861 170
functions, including lambdas themselves. For example:
paul@810 171
paul@810 172
{{{#!python numbers=disable
paul@810 173
def f(x):
paul@810 174
    return lambda y, x=x: lambda z, x=x, y=y: (x, y, z)
paul@810 175
}}}
paul@810 176
paul@861 177
Here, within a module `M`, the outer lambda would have the object path
paul@861 178
`M.f.$l0` whereas the inner lambda would have the object path `M.f.$l0.$l0`.