micropython

docs/invocation.txt

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