micropython

Annotated docs/invocation.txt

502:8bf8faf18dbe
2012-05-11 Paul Boddie Added a _random placeholder module.
paul@69 1
Invocations in classic Python:
paul@69 2
paul@69 3
  f(1, 2, 3)      # positional
paul@69 4
  f(1, 2)         # positional with defaults
paul@69 5
  f(1, 2, c=3)    # keywords
paul@69 6
  f(1, c=3)       # keywords with defaults
paul@69 7
  f(1, 2, 3, 4)   # extra positional arguments
paul@69 8
  f(1, 2, 3, d=4) # extra keyword arguments
paul@69 9
  f(1, 2, *args)  # positional bundles (possibly with defaults)
paul@69 10
  f(1, 2, **kw)   # keyword bundles (possibly with defaults)
paul@69 11
paul@69 12
  Note that f is never fixed before run-time in Python.
paul@69 13
paul@92 14
Comparison to invocations in C:
paul@69 15
paul@69 16
  f(1, 2, 3)      # positional, f known at compile-time
paul@69 17
  f(1, 2, 3)      # positional, f is appropriate function pointer
paul@69 18
                  # ie. (*f)(A, B, C)
paul@69 19
paul@213 20
Least expensive cases (positional plus defaults):
paul@69 21
paul@109 22
  f(1, 2, 3)      # put arguments in frame
paul@69 23
                  # if f is not known, add arguments vs. parameters check
paul@69 24
  f(1, 2)         # to handle defaults, introduce default "filling" where
paul@69 25
                  # not enough arguments are given
paul@69 26
                  # if f is not known, this is obviously done at run-time
paul@69 27
paul@213 28
More expensive cases (keywords plus defaults):
paul@69 29
paul@109 30
  f(1, 2, c=3)    # prepare frame using parameter details
paul@69 31
                  # (provided c is a known parameter)
paul@69 32
                  # if f is not known, this is obviously done at run-time
paul@69 33
  f(1, c=3)       # as with the previous case, with default "filling" done
paul@69 34
                  # where not enough arguments are given
paul@69 35
                  # if f is not known, this is obviously done at run-time
paul@69 36
                  # but with all defaults copied in before keywords are
paul@69 37
                  # assigned (since their positions and thus the positions
paul@69 38
                  # of missing parameters cannot be known)
paul@69 39
paul@213 40
Awkward cases (extra arguments):
paul@69 41
paul@213 42
  f(1, 2, 3, 4)   # put arguments in frame
paul@214 43
                  # if f is not known, add arguments vs. parameters check;
paul@214 44
                  # to handle superfluous arguments, make a suitable object
paul@214 45
                  # and fill it with all such arguments
paul@213 46
paul@213 47
Very awkward cases:
paul@213 48
paul@69 49
  f(1, 2, 3, d=4) # extra keyword arguments
paul@69 50
  f(1, 2, *args)  # positional bundles (possibly with defaults)
paul@69 51
  f(1, 2, **kw)   # keyword bundles (possibly with defaults)
paul@69 52
paul@69 53
  These cases require additional structures to be created, potentially at
paul@69 54
  run-time.
paul@92 55
paul@92 56
Methods vs. functions:
paul@92 57
paul@92 58
  f(obj, 1, 2)    # f known as function at compile-time:
paul@92 59
                  #   f(obj, 1, 2)
paul@92 60
                  # f known as C.m at compile-time:
paul@92 61
                  #   m(obj "assert isinstance(obj, C)", 1, 2)
paul@98 62
                  # f not known at compile-time:
paul@92 63
                  #   f(<context>, obj, 1, 2) for instance-accessed methods
paul@92 64
                  #   f(obj, 1, 2) for class-accessed methods
paul@92 65
                  #   f(obj, 1, 2) for functions
paul@92 66
paul@92 67
  (Could either have universal context usage even for functions, which would
paul@92 68
   ignore them, or attempt to remove contexts when functions are called.)
paul@92 69
paul@98 70
Argument lists for functions:
paul@98 71
paul@98 72
  f(obj, 1, 2)    # f known as function at compile-time
paul@98 73
paul@234 74
    f   -> f (context is null)
paul@98 75
    obj -> argument #1
paul@98 76
    1   -> argument #2
paul@98 77
    2   -> argument #3
paul@98 78
paul@98 79
Argument lists for methods:
paul@98 80
paul@98 81
  f(obj, 1, 2)    # f known as C.m at compile-time (context is C)
paul@98 82
paul@234 83
    f   -> C.m (context is class C)
paul@234 84
    obj -> argument #1 (must be tested against the context)
paul@98 85
    1   -> argument #2
paul@98 86
    2   -> argument #3
paul@98 87
paul@98 88
Argument lists for methods:
paul@98 89
paul@98 90
  f(obj, 1, 2)    # f known as C.m at compile-time (context is an instance)
paul@98 91
paul@98 92
    f   -> C.m
paul@98 93
        -> context is argument #1
paul@98 94
    obj -> argument #2
paul@98 95
    1   -> argument #3
paul@98 96
    2   -> argument #4
paul@98 97
paul@109 98
Argument lists for classes:
paul@109 99
paul@109 100
  f(obj, 1, 2)    # f known as C at compile-time
paul@109 101
paul@234 102
    f   -> instantiator of C
paul@234 103
        -> (argument #1 reserved for a new instance made by the instantiator)
paul@137 104
    obj -> argument #2
paul@137 105
    1   -> argument #3
paul@137 106
    2   -> argument #4
paul@137 107
paul@234 108
  The new instance must be provided as the result of the call.
paul@109 109
paul@98 110
Argument lists for unknown callables:
paul@98 111
paul@98 112
  f(obj, 1, 2)    # f not known at compile-time
paul@98 113
paul@98 114
    f   -> f
paul@98 115
        -> load context for argument #1
paul@98 116
    obj -> argument #2
paul@98 117
    1   -> argument #3
paul@98 118
    2   -> argument #4
paul@98 119
paul@98 120
  Then, check the context and shift the frame if necessary:
paul@98 121
paul@234 122
    f is class: no change
paul@234 123
paul@234 124
    <context> is class:
paul@98 125
      (<context>, obj, 1, 2) -> (obj, 1, 2)
paul@98 126
paul@98 127
    <context> is instance: no change
paul@98 128
paul@137 129
Argument lists in instantiators:
paul@137 130
paul@137 131
  f(obj, 1, 2)    # f not known at compile-time
paul@137 132
paul@137 133
    f   -> C.__new__ (known and called at run-time)
paul@230 134
        -> load context for argument #1
paul@230 135
    obj -> argument #2
paul@230 136
    1   -> argument #3
paul@230 137
    2   -> argument #4
paul@137 138
paul@426 139
  f(obj, 1, 2)    # f known at compile-time
paul@426 140
paul@426 141
    f   -> C.__new__ (known and called at run-time)
paul@426 142
        -> argument #1 left blank
paul@426 143
    obj -> argument #2
paul@426 144
    1   -> argument #3
paul@426 145
    2   -> argument #4
paul@426 146
paul@426 147
Frame re-use in instantiators:
paul@426 148
paul@137 149
  Need to call C.__init__(<instance>, obj, 1, 2), preferably with the existing
paul@137 150
  frame:
paul@137 151
paul@230 152
    *** -> instance overwrites argument #1
paul@230 153
    obj -> argument #2
paul@230 154
    1   -> argument #3
paul@230 155
    2   -> argument #4
paul@137 156
paul@137 157
  Then jump without switching frames.
paul@137 158
paul@426 159
  If no context argument (or blank argument) were provided, a new frame would
paul@426 160
  need to be allocated and filled with a new instance and all remaining
paul@426 161
  arguments from the current frame.
paul@426 162
paul@110 163
Defaults for unknown callables:
paul@110 164
paul@110 165
  f(obj)          # f not known at compile-time
paul@110 166
paul@110 167
    f   -> f
paul@110 168
        -> load context for argument #1
paul@110 169
    obj -> argument #2
paul@110 170
paul@110 171
  Then, check the number of arguments and the availability of defaults against
paul@110 172
  the details provided by the callable's structure.
paul@110 173
paul@111 174
Checking defaults for unknown callables:
paul@111 175
paul@111 176
  Approach #1 - pre-fill defaults, add arguments, check frame
paul@111 177
paul@111 178
  Approach #2 - add arguments, add defaults while checking frame
paul@111 179
paul@331 180
Dynamic functions:
paul@233 181
paul@331 182
  def f(x):
paul@331 183
    def g(y=x):         # dynamic: y depends on non-constant value
paul@331 184
      ...
paul@331 185
    def h(y=2):         # static: y depends on constant value
paul@331 186
      ...
paul@331 187
paul@331 188
  def f(x):
paul@331 189
    g = lambda y=x: ... # dynamic: y depends on non-constant value
paul@331 190
    h = lambda y=2: ... # static: y depends on constant value
paul@233 191
paul@331 192
Representation of dynamic functions:
paul@331 193
paul@331 194
  f = lambda x, y=nonconst: ...
paul@331 195
paul@331 196
  def f(x, y=nonconst):
paul@331 197
    ...
paul@233 198
paul@331 199
  Defines instance with method:
paul@331 200
paul@331 201
    def <lambda>(<context>, x, y=nonconst):
paul@331 202
      ...
paul@331 203
paul@331 204
    def f(<context>, x, y=nonconst):
paul@233 205
      ...
paul@233 206
paul@233 207
  Where default is attribute #1.
paul@233 208
paul@233 209
  f(obj)          # f not known at compile-time
paul@233 210
paul@233 211
    f   -> f
paul@233 212
        -> load context for argument #1 (f, since an instance is referenced)
paul@233 213
    obj -> argument #2
paul@233 214
paul@92 215
Functions as methods:
paul@92 216
paul@92 217
  def f(x, y, z): ...
paul@92 218
  class C:
paul@92 219
      m = f
paul@92 220
  c = C()
paul@92 221
  ...
paul@92 222
  f(obj, 1, 2)    # no restrictions on obj
paul@92 223
  obj.m(1, 2)     # f(obj, 1, 2)
paul@92 224
  C.m(obj, 1, 2)  # f(obj "assert isinstance(obj, C)", 1, 2)
paul@123 225
paul@123 226
Context propagation:
paul@123 227
paul@123 228
  fn = C.m        # has context C
paul@123 229
  fn(obj, 1, 2)   # non-instance context -> explicit context required
paul@123 230
                  # must perform isinstance(obj, C)
paul@123 231
  fn = c.m        # table entry for m on C -> replace context
paul@123 232
                  # gives context c
paul@123 233
  fn(1, 2)        # instance context -> no explicit context required
paul@123 234
                  # context c inserted in call
paul@214 235
paul@214 236
Star parameters are a convenience:
paul@214 237
paul@214 238
  max(1, 2, 3)    # call to max(*args) where args == (1, 2, 3)
paul@214 239
  max((1, 2, 3))  # but why not just do this instead?
paul@214 240
paul@214 241
  One motivation: avoid explicitly making sequences.
paul@214 242
  Opportunity: avoid expensive dynamic allocation of sequences?
paul@214 243
paul@255 244
Star parameters, approach #1:
paul@255 245
paul@255 246
  Make a sequence to hold the extra arguments, either in the caller for known
paul@255 247
  callables or in the function itself.
paul@255 248
paul@255 249
  Such a sequence would need allocating and its contents copying from the
paul@255 250
  stack.
paul@255 251
paul@255 252
Star parameters, approach #2:
paul@255 253
paul@255 254
  Keep the extra arguments in the stack.
paul@255 255
paul@255 256
  Access to the star parameter would need to consider assignment to other
paul@255 257
  things and "escape situations" for the parameter:
paul@255 258
paul@255 259
    def f(*args):
paul@255 260
        return args # need to allocate and return the sequence
paul@255 261
paul@255 262
  Access to elements of the extra argument sequence would behave slightly
paul@255 263
  differently to normal sequences, but this could be identified at
paul@255 264
  compile-time.
paul@255 265
paul@255 266
Star parameters, known callables and sequences, approach #1:
paul@214 267
paul@214 268
  g(1, 2, 3, 4)   # g known as function g(a, *args) at compile-time
paul@214 269
paul@214 270
    g   -> don't get any context information
paul@214 271
    1   -> argument #1
paul@214 272
    2   -> reference to sequence containing arguments #2, #3, #4
paul@214 273
paul@255 274
Star parameters, known callables and sequences, approach #2:
paul@255 275
paul@255 276
  g(1, 2, 3, 4)   # g known as function g(a, *args) at compile-time
paul@214 277
paul@255 278
    g   -> don't get any context information
paul@255 279
    1   -> argument #1
paul@255 280
    2   -> argument #2
paul@255 281
    3   -> argument #3
paul@255 282
    4   -> argument #4
paul@255 283
paul@255 284
Star parameters, unknown callables, both approach #1 and #2:
paul@214 285
paul@214 286
  g(1, 2, 3, 4)   # g not known at compile-time
paul@214 287
paul@214 288
    g   -> g
paul@214 289
        -> load context for argument #1
paul@214 290
    1   -> argument #2
paul@214 291
    2   -> argument #3
paul@214 292
    3   -> argument #4
paul@214 293
    4   -> argument #5
paul@214 294
paul@214 295
  Then, check the context and shift the frame if necessary (described above).
paul@214 296
paul@214 297
  If g has a star parameter - g(a, *args) - then...
paul@214 298
paul@214 299
  Approach #1 - move arguments #3, #4, #5 (or shifted to #2, #3, #4) into a
paul@214 300
                sequence, adding a reference to the sequence in their place
paul@214 301
paul@214 302
  Approach #2 - maintain special access rules to arguments #3, #4, #5 (perhaps
paul@214 303
                shifted to #2, #3, #4) as a C-like array
paul@214 304
paul@214 305
Tradeoffs for star parameter approaches:
paul@214 306
paul@214 307
  Approach #1 - potentially costly at run-time as arguments need moving around,
paul@214 308
                but the arguments would behave normally in functions
paul@214 309
paul@214 310
  Approach #2 - need to track usage of the star parameter and to possibly copy
paul@214 311
                its contents if assigned, as well as providing special access
paul@214 312
                mechanisms, but the invocation procedure would be simpler