4 ms·
Even without tail recursion, conventional procedure calls are still just gotos plus an automatically-managed stack. Which fits the author's thesis that higher-l
by dreamcompiler 7y ago
Even without tail recursion, conventional procedure calls are still just gotos plus an automatically-managed stack. Which fits the author's thesis that higher-level memory models are what distinguishes higher-level programming.
The aspect of this that bothers me is that automatically-managed stacks are not the only way to call procedures. Continuations for example are another way; actors yet another. But because automatically-managed stacks are present in virtually every CPU at the ISA level, many programmers never venture outside that model and have trouble understanding that others even exist, much less what advantages they might bring.
- CyberDildonics 7y agoAnd what are those advantages? Function call ABIs are a standard way to change what is executing and come back when it is done. Does that really need to change? Other software architecture can be built on top of that, you don't need to change the way you call a function with assembly to structure software differently.
- kragen 7y agoThe advantages are that you can do threads, exceptions, lazy sequences, backtracking, tail-call elimination for state machines, and so on. (The disadvantages are the same.) Yes, you can emulate these on top of function-call-based ABIs, but you incur the usual emulation overhead.
- cat199 7y agothis sub-discussion about tail calls, etc. is in reference to: https://dspace.mit.edu/bitstream/handle/1721.1/5753/AIM-443.pdf https://dspace.mit.edu/bitstream/handle/1721.1/5753/AIM-443.... that series of papers was released adjacent to the development of the scheme language which will generally optimize out the need for a call stack and replace it with essentially goto in certain cases. as for 'changing the way you call a function' this subtopic is primarily of importance for language/compiler implementers; while you don't "need" to change the way you call a function, often doing so can speed up the program hugely, or allow things that were not otherwise possible (e.g. infinite recursion without stack overflow)
- dreamcompiler 7y agoTail call elimination isn't always possible, but it's the gateway drug to continuations which are always possible, and continuations are the gateway drug to actors, and once you have actors you're free to move any element of your program anywhere, break it into pieces and put them back together, go nuts, it just works--and now you're able to write code on asynchronous million-node networks without having to worry about locks, mutating global data, shared stuff, and everything else that gets in the way of using modern hardware.
- tsimionescu 7y agoIf you have code that doesn't need to mutate data, it is already easy-ish to write on whatever scale you want. The problem is that there are very few algorithms and business problems that don't require shared state to work. I'm not sure how actors or continuations could solve the problem of creating a distributed database of clients, or even of computing histograms on data in parallel, significantly more easily than more traditional programming paradigms.
- toast0 7y agoRather than accessing the shared data directly, you send a message to the actor that owns that piece of shared data, that message could be a request for some data, or a request to modify it in some way. The actor that owns the data reads its mailbox of requests and processes them without worrying about locks and things, because it has exclusive access. The actor that needs to access the data doesn't worry about locks and things because it's not directly accessing it, it's just sending a message. The system worries about locks and things, because the message mailboxes are shared state to some extent. If you can cleanly separate ownership of different pieces of data into different actors, you get parallelism. If there's too much dependency between the pieces of data, so that separating ownership makes things more complex, maybe that becomes more visible.
- tsimionescu 7y agoThat sounds nice, but it means that all reads and writes are serialized. You can't scale a system like this to, say, work for sign-up and retrieval of all Gmail users.
- gumby 7y agoThere are significantly more calling semantics beyond “push a frame and branch” as Kragen enumerates. Some, like coroutines, are visible to the high-level programmer; others, such as tail call optimization, might be invisible.
- gumby 7y agoNot sure what you mean by “automatically managed” since the function preamble (before user code is run) saves registers etc. well, the abi specifies if the caller or callee does it but it’s a much of a muchness.
- dreamcompiler 7y agoI mean saving the address for control to return to so that the subroutine never has to know or care; it just executes the RET instruction. And RET is just POP+JMP. This is super handy--so handy that alternative control strategies aren't explored as much as they might have been otherwise.
- Avi-D-coder 7y agoIf it makes you feel any better, I'd bet you all 3 UFOs still use call stacks. Even aliens are stuck on 8086 esque CPUs ;)