If you have big complicated objects, there are generally several logical chunks it could be sliced up into to ensure the memory bus is pretty much only copying information useful to whatever its currently doing. Note this does not generally apply to scripting languages like python as those are so horrendously bad on the memory bus by default that there is nothing you can do about it.
Does numpy or other such code that's a wrapper around systems programming language evade this limitation?
See mike acton's cppcon 2014 talk where he disparages object oriented design and talks about efficiency in general at the tail end of the golden age of video games (not that anyone knew that at the time):
https://www.youtube.com/watch?v=rX0ItVEVjHc
I've seen quite a few demos on Commodore 64 and Amiga and figured out how to run the demos on my own which is somehow more "magical" even if it is through an emulator because you have to put in a little effort vs. just hitting the play button on a video. It's quite impressive what functionality the coders of demos (and some games too) managed to (
EDIT: and still manage to) cram into what is now considered severely lacking hardware.
Recursion is generally considered kindof haram since you are blowing out the stack oftentimes unnecessarily
Tail calls were just mentioned but YMMV on whether you get it. It really got its start with Scheme AFAIK and I believe the Scheme standard requires tail call optimization to be implemented when applicable. Racket has probably the most well-developed Scheme implementation around (
multiple languages now, actually) and has TCO. Python doesn't have TCO natively but
some guy figured out how to implement it using a decorator. Realistically though if I wanted to implement
fac(n) in Python I'd just use a
while loop rather than using that hack or expanding the maximum recursion depth. OCaml, which is a functional programming language that prefers immutable data but lets the user use mutable data without faggy category theory concepts, has TCO and I suspect that's probably par for the course in most FP languages today.
Functional is really good when you're doing async stuff because you can better guarantee the state of the data a given function accesses when you don't have 20+ functions writing to and reading from the same area in memory.
I understand Clojure really focuses hard on that sort of thing but the thing about Clojure is that error messages come from transpiled Java code rather than from the Clojure source. The line numbers don't even match. That was more or less an immediate deal-breaker. Erlang and Elixir also have FP and a huge async focus but I really haven't used either.