Lichen

Annotated docs/wiki/Structure

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