Hacker News new | past | comments | ask | show | jobs | submit login

‘Never use recursive functions’ is one of those stories that comes up in embedded. For automotive work it is one of the SAE’s ground rules.

Since The 1980s, when LISPers decried Microsoft BASIC’s hegemony we were told that recursive functions are a superior answer. They are for some things, but as soon as you start coding in assembly (pure assembly, not a few lines of Assembly to optimize a C program) you start to see stack calling conventions as a problem as much as a solution.




It's good to distinguish between general-recursive and tail-recursive functions. Tail-recursive functions can be transformed into code that uses a bounded amount of stack (no more than a single call worth). The latter kind of functions could be used freely on systems where general recursion is forbidden, and sometimes are a clearer or more succinct way to specify the required behavior (and sometimes not)

Some lisp-adjacent languages like clojure use a special syntax for tail-recursive functions[0], so that you document and know at compile time that you don't have a general recursive function.

[0]: https://clojuredocs.org/clojure.core/recur


Tail recursions are just very badly documented while loops. In a while loop it is very clear if you use memory or not, in a tail recursion you have to inspect the code and realize if it is tail recursive or not, and if you do it wrongly you just accidentally used a lot of memory for the stack. And tail recursions doesn't do anything you can't do in a while loop, recursions only power is the power to use the stack, without it they don't add anything.


There are other sorts of data structures that interest me.

A number of my Arduino projects break down into definitive layers such as something that spools several threads of graphical data out of the program memory, processes it to make a line for a persistence of vision display, then either flushes pixel data via SPI to a lightstrip or sends the data via the serial line to my PC for testing.

If I recoded it in AVR8 assembly I would have no stack or reenterant or recursive subroutines. Everything would be allocated statically or would go in a register, depending on the graphic data you need a variable number of cursors allocated (like the way dimensions of arrays were set in the configuration of a FORTRAN program long ago.)

I haven't recoded it in assembly because I'm still thinking I might need to switch to a more capable board with a different CPU and the portability makes up for C being a very annoying language (e.g. the stack, calling conventions, and all of that is a problem instead of a solution for the programs I write)

(I think maybe... If performance isn't good enough I'll run a soft AVR8 inside an FPGA and offload the hard parts to the FPGA but that is going from the frying pan to the fire.)

Another alternative to the stack is the approach used by asyncio in Python and many similar frameworks where you maintain flat contexts of execution that can be managed at arms length by some kind of dispatcher but that are executed intimiately by coroutines.




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: