(This is drifting fairly far off topic and probably should move
to comp.programming or some such. But for now...)
There is one bit of semantics I would very much like: partial
application. ... C does not have nested functions
C does not, but GNUC does.
so a good part of the cost (often called building a lexical closure)
is not required and I am sure[1] that a reasonably cheap implementation
would be possible.
There are two most common (in my experience) parameter-passing
mechanisms used in C: "on the (typically hardware-provided) stack"
and "in registers". If one were to borrow GCC's "trampoline"
technique for its nested functions, I think you are correct here.
But there is one "gotcha".
Partial application solves this problem in the following way: Given a
function with this prototype:
RT function(T1 p1, T2 p2, T3 p3);
A function call like function(a1) is an expression that evaluates to a
pointer to a function. The type being:
RT (*)(T2, T3)
[and so on]
On machines with "parameters on the stack", the parameters are
typically pushed in reverse. On machines with "parameters in
registers", the first N parameters (for some machine-dependent
constant N) are in hardware registers -- though this gets more
complicated for machines with separate floating-point registers --
and then any excess parameters are delivered on a stack, or in a
"parameter block" pointed to by yet another register, or similar.
Note that in the proposal you have above, partial application
applies exclusively to the "front-most" (or left-most) parameters.
If you supply one argument, it must be the first one, and you are
left with a pointer to a function taking the remaining two arguments;
if you supply two, they are the first two, and you get a pointer
to a function taking just the third.
For stack-based systems, however, one "wants" (internally) for the
applied parameters to start at the end and work towards the beginning.
(For register-based systems, there are no such restrictions, as
long as you do not go past the number of "register-ized" parameters.)
Imagine we are generating code for a stack-based system. Suppose
also that we have the ability to create code at runtime, by shoving
instructions into memory and then using whatever facilities the
system provides to mark the new section as "executable code". When
the compiler sees function(a1), it can then emit, e.g.:
push #24 /* after calculating that we need 24 bytes */
call __get_memory_for_trampoline /* result in a0 */
mov a0, a1 /* save result */
mov #POP_TO_TMP_REG, (a1)+ /* pops return address */
mov #PUSH, (a1)+ /* push-with-value */
mov arg1, (a1)+ /* the parameter we want */
mov #PUSH_TMP_REG, (a1)+ /* restore return-address */
mov #JUMP, (a1)+ /* then jump-indirect */
mov #function, (a1) /* to target function */
/* now translate from "RAM whose address is in a0" to
"RAM containing executable code" (returned code-address
is also in a0, which may or may not differ from the
supplied data-address) */
push #24 /* size of region, again */
push a0
call __translate_to_executable
mov a0, -20(fp) /* save resulting function pointer */
What this means, though, is that our "pre-evaluated" argument is
now supplied as the *last* argument to the function, because the
"not pre-evaluated" arguments are pushed by the indirect call:
push arg2
push arg3
mov -20(fp),a0
call (a0) /* call the trampoline */
Presumably this is why you suggested adding a special marker syntax:
Because the parameter passing/access mechanism is likely to be
slightly more expensive than for ordinary parameters, it would be
acceptable to identify those initial parameters that may be passed
in this new way by, for example, but marking them off with a ";":
void my_func(int a, double b; char *s);
(On the other hand, perhaps you were thinking of doing this without
trampolines, by "smuggling" an extra "void *" argument pointing to
the equivalent of a closure block.)