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