Presently, parameters are pushed onto a heterogenous stack.
The idea of parameter lists was borne out of:
The idea of exceptions was borne out of:
Return values are placed on the stack.
The idea of return values was borne out of:
Using the current system, the compiler must do extra work:
A stack is just an optimization of a list.
A list is a collection.
The concept of stack conflates several issues:
Scoping is a semantic issue.
Optimization is an issue for optimization engineers. Low-level optimization should not appear in an architecture.
I argue that every type should have its own stack(s). [I find that two stacks - input stack and working stack - helps programming].
Every type might be stored in a separate collection.
The parameters to a routine might be a collection of typed collections.
Assertion: if something is in a typed collection, then it has the correct type.
Type check is done at "push" time (where "push" means to add the item to its collection).
What can we do if two parameters have the same type?
Presently, we use variables and assign a type to each.
Another solution might be to allow type synonyms, where each parameter gets a unique name, but the name is synonymed to be of a given type.
fn(a : int, b : int)
a = int
b = int
The fact that a (and b) is an int, is an implementation detail, and should not appear in the Architecture. The fact that we want to use automation to check such details implies that the semantic (i.e. architecture-related) and implementation details might need to appear in the same body of code. A trade-off is to separate implementation details from architectural details, as above. This allows the Architect to describe an architecture at the architectural level while pushing implementation details to more innocuous places (like the bottom of the file). The Engineer(s) and Implementor(s) can later add such details without affecting the readability of the original architecture.
At the architectural level, parameters to functions are not variables. Variables and their types are implementation details. The above arrangement suggests that we can expunge variables from parameter lists completely.
The current style of assigning types to parameters in a function signature conflates two issues (1) architectural readability and, (2) implementation details.
In the above, every parameter, at the DI level, is a type.
I have found that treating parameters as types to be defined later, reduces syntactic noise. Code is shorter and not cluttered with detail.
[The notion of types associated with parameters was based on premature optimization - making it less expensive in memory and CPU time to define parameters and their implementations. With "infinite" CPU power and "infinite" memory, there is no need to conflate types with their implementations in function signatures. Types can be defined in a scoped manner - defined in terms of other types and, ultimately, as consisting of a small number of opaque types. So-called system languages are concerned with implementation details. Even programs written in such languages have distinct DI and implementation aspects.]
In the above notation, implementation of types is the exception, not the rule.
I have implemented a version of the above ideas and will document the operations (about 5) that I use (push-and-check-type, pop, list-add, etc.).
The PT Pascal compiler, written in the language S/SL, uses stacks for scoping and type checking. The PT Pascal source code and S/SL source code can be found at https://research.cs.queensu.ca/home/cordy/pub/downloads/ssl/.
[A version of S/SL with input and output parameters was documented in a thesis - I can't find my copy of the thesis nor can I remember the author's name.]
 Parameters of all types share the same stack.
 This idea, of separation, also appears in Ohm-JS, where the parse (grammar) is separated from the semantic details.
 DI mean Design Intent - an explicit description of architecture.