Lichen

Annotated docs/wiki/Restarted

988:cfdcae64b86f
14 months ago Paul Boddie Merged changes from the trailing-data branch. value-replacement
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.