[Python-ideas] code segments
spir
denis.spir at gmail.com
Thu Feb 20 14:10:37 CET 2014
I often find myself needing to wrap little pieces of code into a whole "object"
(programming element or value), sometimes with a name. I feel the same kind of
lack as an FP programmer having to code in a language without function
"objects"; and indeed the situation is very similar.
The cases where i need that in fact follow a schema, let's call it "event". In
games, an event is usually something that happens in a particular situation:
there is a logic like situation --> happening, an event is in fact the binding
of 2 elements. We find something similar with automation (which also is
event-driven) sequences: a tree of stages which are reached under a condition
and each command a given action: condition --> action. More generally, one may
find a kind of cause --> effect schema. An event can be conceptualised as a
{cause effect} pair. But what are the pair's elements? And how to encode them in
a program?
monster'appears : Event{
cause : and monster.is'hidden (= character.pos (33 99))
effect :
monster.move 37 101
monster.groar 'fiercefully
monster.is'hidden :: false
character.scream
}
The cause is conceptually a logical expression; but it can be arbitrarily
complex --thus may require multiple statements if only for readability, also for
debugging or other "meta" needs. Otherwise, it is like a proper function, with
an output (here a logical value) and no effect. But it does not take any input!
instead, any pieces of data it uses are present in the surrounding scope: they
_must_ be there, if i may say by logical necessity.
The effect is an action, like a procedure that changes the world and computes no
product. Similarly, it takes no input but finds its data in the outer scope.
Thus, we have 2 kinds of input-less procedures. Otherwise, the notion is
somewhat like Ruby blocks I guess. This is also similar to the recently proposed
"inline" functions on python-ideas (reason why cc to this list).
There may also be a relation to dynamic scoping, since such code segments in
fact appear to take their input from the caller's scope: but it is not a
_caller_, instead a kind of surrounding block and scope, like in case of
inlining. I think _this_ kind of semantics, similar to inlining, is the actual
value of dynamic scoping, and what we may miss with static scoping only and
ordinary procedures only. We may need a way to have "reified" blocks of code
executed in the surrounding scope as if they were explicitely written there, or
inlined (or C-like macros).
Actual functions/procedures would be overkill, since there is no stack frame (or
similar), thus all the typical "prologue" and "epilogue" of procedure calls is
unneeded. We just need to jump there, wherever the code lies in memory, and come
back. Or, inline à la C, or like C macros, but this in my view does not match
the semantics.
What I'm considering is code segment "objects" like procedures but without:
1. input variables,
2. local scope,
3. call/return [0],
three related points in fact.
Even more generally, one may note ordinary "functions", in mainstream langs and
FP alike, combine a number of properties, the ones listed above plus:
0. more basically, general notion of a block of code as programming element
4. in addition, possible output product, for a function-like code segment
(maybe I miss some)
For the present notion of code segments, all i need is 0, plus 4 in some cases. [2]
I'm not sure at all this is a good idea, however --but I like it and would
regularly use it if available.
d
[0] With register save, new stack frame construction, param passing (prologue),
etc, and undoing of all that.
[1] A static location would not be thread safe; the only other possibility I can
think of is dynamic allocation on the heap: overkill.
A real alternative is _not_ passing the product back, but noting that at a low
level, there are no functions, instead only actions that write somewhere. Eg:
x := DIV y z
actually means:
DIV x (y z)
(and in fact at an even lower level x is y, so we get "DIV x z")
Similarly, when part of a higher expression we need temp storage location, eg:
a := SUM b (DIV c d)
requires:
DIV temp (c d)
SUM a (b temp)
Also:
IF (EQUAL a b) THEN do-that
gives:
EQUAL temp (a b)
IF temp THEN do-that
Function-like code segments would take the place of explicit expressions here.
For instance:
a := segment-name
a := SUM b segment-name
if segment-name then do-that
Thus, a rewriting rule can get read of their output altogether, I guess.
[2] Detail: in a low-level language, it may be easy to do that: a pair of goto;
but the instruction for jumping back must accept a _variable_ target, and the
the forward one may also be variable at times. In assembly, it's jumping to
given addresses, or even just setting the PC register (program counter); there
is no issue of variable targets at this level, they're variable adresses in
general, meaning pointers. The only issue is scope restriction on such jumps
(whether and how to possibly jump into other memory segments). A final point is
the output of function-like code segments: we may need a single, temporary stack
slot for it [1]. All these issues are solved for ordinary procedures.
More information about the Python-ideas
mailing list