1 = Lichen Restarted = 2 3 Originally, lots of work was being put in to support various Python features that are arguably superfluous. The realisation was had that a lot of effort was being made for little practical benefit by trying to support things that are, [[../Design|in the larger picture]], not that important. Consequently, Lichen was refocused on a smaller set of more useful Python features. 4 5 This document is of historical interest only, with the [[../Design|design]] and other documents attempting to communicate the results of this restarting effort. Some obsolete information is therefore preserved below. For example, attributes hold context information in the diagrams, but context information is now held in wrappers or is maintained separately within programs. 6 7 Objectives: 8 9 * Individual module inspection 10 * No importing triggered during module inspection 11 * All unresolved external references are set to `<depends>` 12 * Hierarchical module namespaces are not exposed in programs 13 * Modules are independent: package hierarchies are not traversed when importing 14 * Nested scopes will be dropped 15 * If expressions and comprehensions will be dropped 16 * `self` is a reserved name and is optional in method parameter lists 17 * Unbound methods must be bound using a special function taking an instance 18 * Functions assigned to classes do not become unbound methods 19 20 == Names == 21 22 Names are locals, globals or built-ins. (Special names exist internally to support certain operations.) 23 24 Locals inside functions are dynamic; locals outside functions are static, as are module globals. Built-ins are defined statically in the `__builtins__` package. 25 26 == Imports == 27 28 Imports provide access to external references. The "leaf" module in a module path is the module returned by the statement. 29 30 Indicate that module "compiler" is accessed via compiler... 31 {{{#!python numbers=disable 32 import compiler 33 }}} 34 35 Indicate that module "compiler" is accessed via comp... 36 {{{#!python numbers=disable 37 import compiler as comp 38 }}} 39 40 Indicate that module "compiler.ast" is accessed via ast; module "compiler.transformer" is accessed via tr... 41 {{{#!python numbers=disable 42 import compiler.ast as ast, compiler.transformer as tr 43 }}} 44 45 Import compiler.ast, access Function... 46 {{{#!python numbers=disable 47 from compiler.ast import Function 48 }}} 49 50 Import compiler.ast, access Function as F... 51 {{{#!python numbers=disable 52 from compiler.ast import Function as F 53 }}} 54 55 This causes some semantic differences with Python, with the most significant one being the following: 56 57 {{{{#!table 58 '''Python''' || '''Lichen''' 59 == 60 <style="vertical-align:top"> 61 62 Import compiler, import compiler.ast, set ast on compiler... 63 {{{#!python numbers=disable 64 import compiler.ast 65 }}} 66 ...returning compiler 67 68 || 69 <style="vertical-align:top"> 70 71 Import compiler.ast... 72 {{{#!python numbers=disable 73 import compiler.ast 74 }}} 75 ...returning compiler.ast as ast 76 77 }}}} 78 79 Some statements can be rewritten to achieve the same effect: 80 81 {{{{#!table 82 '''Python''' || '''Lichen''' 83 == 84 <style="vertical-align:top"> 85 86 Import compiler, access ast as submodule... 87 {{{#!python numbers=disable 88 from compiler import ast 89 }}} 90 91 || 92 <style="vertical-align:top"> 93 94 Import compiler.ast... 95 {{{#!python numbers=disable 96 import compiler.ast 97 }}} 98 ...returning compiler.ast as ast 99 100 }}}} 101 102 Other kinds of import are not directly possible with Lichen. For example: 103 104 Import all names from compiler.ast... 105 {{{#!python numbers=disable 106 from compiler.ast import * 107 }}} 108 109 Some notes: 110 111 * Names not defined in a module and not declared in import statements are unresolved 112 * Modules are identified during inspection but are not loaded 113 * Instead, modules are added to a list and are imported later 114 * Names imported from modules are set to `<depends>` (since the contents of modules will not generally be known) 115 * Names are resolved in a later activity 116 117 == Self == 118 119 In Python: 120 121 * The `self` name provides access to the instance associated with a method 122 * The instance is supplied by a "context", initialised when a method is obtained from an instance (or through other attribute accesses) 123 * Upon invocation, any instance context must be assigned to the `self` parameter, provided the callable is a method 124 * Meanwhile, any non-instance context is not assigned to the `self` parameter, which should be provided explicitly for class-accessed methods 125 * Plain functions never expose `self` or have `self` initialised, even if they have been assigned to an instance 126 127 Apart from tests for the nature of the context and the callable, the argument list is effectively variable. 128 129 With `self` as a ubiquitous, hidden parameter: 130 131 * The `self` name still provides access to the instance associated with a method 132 * The instance is still supplied by a "context", initialised when a method is obtained from an instance (or through other attribute accesses) 133 * Upon invocation, `self` is included in the argument list regardless of the nature of the callable 134 * Class-accessed methods would have their class as context, following from the above, but `self` may not refer to a class: it must be an instance 135 * To combine class-accessed methods with instance contexts, a special function is employed 136 137 The argument list for each callable thus remains static, at a cost of allocating an extra argument that may not be used. (Various calling conventions for certain processor architectures employ potentially unused registers, anyway.) Note that a callable may support defaults, however, and thus any argument list may need extending to include default values for parameters without corresponding arguments. 138 139 {{{{#!table 140 '''Python''' || '''Without self''' 141 == 142 <style="vertical-align:top"> 143 144 {{{#!python numbers=disable 145 inst.method(x, y, z) 146 # -> inst.method(inst, x, y, z) 147 148 def method(self, a, b, c): 149 # self = inst; a = x; b = y; c = z 150 }}} 151 152 || 153 <style="vertical-align:top"> 154 155 {{{#!python numbers=disable 156 inst.method(x, y, z) 157 # -> inst.method(inst, x, y, z) 158 159 def method(a, b, c): 160 # self = inst; a = x; b = y; c = z 161 }}} 162 163 == 164 <style="vertical-align:top"> 165 166 {{{#!python numbers=disable 167 cls.method(self, x, y, z) 168 169 def method(self, a, b, c): 170 # parameters = arguments 171 }}} 172 173 || 174 <style="vertical-align:top"> 175 176 {{{#!python numbers=disable 177 f = get_using(cls.method, self) 178 # context of f = self; value of f = cls.method 179 f(x, y, z) 180 # -> f(context of f, x, y, z) 181 182 def method(a, b, c): 183 # self = context of f = self; a = x; b = y; c = z 184 }}} 185 186 }}}} 187 188 To avoid usage of `self` in undefined ways, only methods are able to use `self` and are not allowed to redefine it. Consequently, when invoking a callable, the context is set (where the callable is unknown until run-time; it is not set if compile-time knowledge indicates that it is not needed), and in situations where `self` is not permitted, the context is therefore safely ignored. Meanwhile, methods are always supplied with a context compatible with `self`. 189 190 || '''Callable''' || '''self''' || '''Remarks''' || 191 || Class || context || context discarded and replaced by allocated instance || 192 || Function || null || `self` not permitted, context ignored || 193 || Function (stored on class) || class as context || `self` not permitted, context ignored || 194 || Function (stored on instance) || instance as context || `self` not permitted, context ignored || 195 || Instance || instance as context || `self` set to instance || 196 || Method (via class) || class as context || method not called (see "unbound methods") || 197 || Method (via instance) || instance as context || `self` set to instance || 198 199 Note that the treatment of functions stored on classes differs from Python. In Python, such functions would become unbound methods (see below) and would employ their first parameter as an effective `self` parameter (regardless of name). 200 201 == Unbound Methods == 202 203 Since methods acquired directly from classes ("unbound methods" in Python) are meant to be combined with an instance as context (using the `get_using` function), they must be uncallable until combined with the appropriate context, yet the same methods when acquired via instances ("bound methods" in Python) must be immediately callable. 204 205 To support the two different states of methods, the principal structure of a class has attributes referencing uncallable versions of its methods. Meanwhile, such uncallable methods reference callable versions and when instances are employed to access the class attributes, it is these callable versions that are retrieved. For example: 206 207 {{{#!graphviz 208 //format=svg 209 //transform=notugly 210 digraph structures { 211 node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Instance and class structures"]; 212 edge [fontsize="13.0",fontname="Helvetica",tooltip="Instance and class structures"]; 213 rankdir=TB; 214 215 instanceC [label="<main> instance of C |{ context of a | value of a }|{context of b | value of b }",shape=record]; 216 classC [label="<main> class C |{ context of m | <m> value of m }|{ context of n | <n> value of n}",shape=record]; 217 callables [label="<m> m\ncallable |<n> n\ncallable",shape=record]; 218 uncallables [label="<m> m\nuncallable |<n> n\nuncallable",shape=record]; 219 220 instanceC:main -> classC:main; 221 classC:m -> uncallables:m [label="C.m",style=dashed]; 222 classC:n -> uncallables:n [label="C.n",style=dashed]; 223 uncallables:m -> callables:m [label="get_using(C.m, instance)",style=dashed]; 224 uncallables:n -> callables:n [label="get_using(C.n, instance)",style=dashed]; 225 } 226 }}} 227 228 The precise structure usage is as follows: 229 230 {{{#!graphviz 231 //format=svg 232 //transform=notugly 233 digraph methods { 234 node [shape=box,fontsize="13.0",fontname="Helvetica",tooltip="Method structures"]; 235 edge [fontsize="13.0",fontname="Helvetica",tooltip="Method structures"]; 236 rankdir=TB; 237 238 classC [label="<main> class C | { context of m | <mvalue> uncallable for m } | ...",shape=record]; 239 uncallableattr [label="attr | { <context> C | <value> uncallable for m }",shape=record]; 240 callableattr [label="attr | { <context> instance | <value> callable for m }",shape=record]; 241 uncallable [label="<main> uncallable for m |{ __fn__ | <b> bound method reference | <fn> unbound method routine }|{ __args__ | minimum #parameters | <ptable> parameter table reference }",shape=record]; 242 callable [label="<main> callable for m |{ __fn__ | 0 | <fn> bound method routine }|{ __args__ | minimum #parameters | <ptable> parameter table reference }",shape=record]; 243 ptable [label="<main> parameter table for m | ...",shape=record]; 244 245 classC:mvalue -> uncallableattr [label="C.m",style=dashed]; 246 classC:mvalue -> uncallable:main; 247 uncallableattr:value -> uncallable:main; 248 uncallableattr -> callableattr [label="get_using(C.m, instance)",style=dashed]; 249 uncallable:b -> callable:main; 250 callableattr:value -> callable:main; 251 uncallable:ptable -> ptable:main; 252 callable:ptable -> ptable:main; 253 } 254 }}} 255 256 Callable methods provide a reference to a callable routine in its special callable member, just as functions and classes do. Uncallable methods populate the callable member with a reference to an error routine. Thus, any attempt to call an uncallable method would cause the error routine to be invoked. In addition, uncallable methods reference the corresponding callable methods so that the callable methods can be found and referenced. 257 258 || '''Accessor''' || '''Provider''' || '''Attribute''' || '''Context''' || '''Summary''' || 259 ||<|4> Instance ||<|4> Instance || Function || ''not used'' ||<|6> Preserve context || 260 || Bound method || Original instance || 261 || Unbound method || Providing class || 262 || Other || Same as value || 263 ||<|4> Instance ||<|4> Class || Function || ''not used'' || 264 || Bound method || Original instance || 265 || Unbound method || Accessing instance, if compatible || Test and replace context || 266 || Other || Same as value ||<|5> Preserve context || 267 ||<|4> Class ||<|4> Class || Function || ''not used'' || 268 || Bound method || Original instance || 269 || Unbound method || Providing class || 270 || Other || Same as value || 271 272 When obtaining an unbound method from an instance attribute, the context of the method attribute is provided. Indeed, the context is always preserved when accessing instance attributes. 273 274 When obtaining an unbound method from a class attribute via an instance, the context of the method attribute is tested against the accessing instance. If compatible, an attribute is copied containing the instance as context and a callable method reference as value. 275 276 When obtaining an unbound method from a class attribute, the context of the method attribute is provided. Indeed, the context is always preserved when accessing class attributes directly. 277 278 When combining an unbound method obtained from a class with an instance using `get_using`, the context of the method attribute is tested against the supplied instance. If compatible, an attribute is copied containing the instance as context and a callable method reference as value. 279 280 === Functions as Unbound Methods === 281 282 Functions not defined within classes could be treated as unbound methods if they were to employ `self` (thus indicating that they are intended as methods). Such functions would then be recorded as uncallable in the module namespace, needing to be explicitly bound to a class using a special function. However, there appears to be limited utility in defining functions in this way, instead of defining them directly as methods, or instead of merely using such generic functions from existing methods.