Modern operating systems that we have (Windows, Linux) work with what I call the "large stack model". And this model is erroneous, sometimes, and motivates the need for "non-compact" languages.
The "large stack model" assumes that the compiled program will allocate "stock frames" for function calls in an adjacent memory region, using machine instructions to quickly configure registers containing a stack pointer (and an optional stack frame pointer). This leads to a quick function call / return, at the price of having a large contiguous area for the stack. Since 99.99% of all programs running under these modern operating systems work well with a large stack model, compilers, loaders, and even the OS "know" about this area of the stack.
One of the common problems that all such applications face is "how big should my stack be?" Since memory is cheaper, the main thing is that a large chunk is allocated for the stack (MS defaults to 1 MB), and the typical structure of application calls never approaches its use. But if the application really uses all of this, it dies with an illegal memory link ("I'm sorry, Dave, I can't do this"), thanks to reaching the end of its stack.
Most so-called “non-compact” languages are not really stacked. They simply do not use the continuous stack provided by these systems. Instead, they populate the stack stack from the heap each time the function is called. The cost of calling one function increases slightly; if the functions are usually complex or the language is interpreted, this additional cost is negligible. (You can also define DAG calls in the program call graph and select a heap segment to cover the entire DAG, so you get both the heap distribution and the speed of the classic calls of the large stack functions for all calls inside the DAG calls).
There are several reasons for using heap allocation for stack frames:
1) If the program performs deep recursion, depending on the specific problem, it solves, it is very difficult to pre-allocate the area of the "large stack" in advance, because the required size is unknown. It is inconvenient to organize function calls to check if there is enough stack left, and if not, redistribute the larger fragment, copy the old stack and adjust all the pointers to the stack; which is so inconvenient that I do not know any implementations. The allocation of stack frames means that the application should never be upset until literally no allocated memory appears.
2) The forks subtask program. Each subtask requires its own stack and therefore cannot use a single "large stack". Thus, you need to allocate stacks for each subtask. If you have thousands of possible subtasks, you may need thousands of “big stacks,” and the memory demand suddenly becomes ridiculous. Highlighting stack frames solves this problem. Often, subtasks "stacks" refer to parent tasks for implementing lexical coverage; as a fork subtask, a "lookup tree" called a "cactus glass" is created.
3) Your language has sequels. They require that the data in the lexical domain, visible to the current function, is somehow stored for later reuse. This can be done by copying the parent stack frames, raising the cactus stack, and continuing.
PARLANSE programming langauge I implemented 1) and 2). I am working on 3).