Although I'm not sure that the widely used Lisps supported automatic memoization, I think there are two reasons why memoization is more common in functional languages, and one more for Lisp-family languages.
First of all, people write functions in functional languages: calculations whose results depend only on their arguments and which do not affect the environment. Anything that does not meet this requirement is generally not amenable to memoirization. And, well, imperative languages are only those languages in which these requirements are not met or cannot be satisfied, because they would not be mandatory otherwise!
Of course, even in just user-friendly languages, such as (most) Lisps, you have to be careful: you probably shouldn't remember the following:
(defvar *p* 1) (defun foo (n) (if (<= n 0) *p* (+ (foo (1- n)) (foo (- n *p*)))))
Secondly, functional languages usually want to talk about immutable data structures. This means two things:
- It is actually safe to memoize a function that returns a large data structure
- Functions that build very large data structures often need a huge amount of garbage because they cannot mutate intermediate structures.
(2) somewhat contradictory: the resulting wisdom is that GCs are now so good that this is not a problem, copying is very cheap, compilers can do magic, and so on. Well, people who wrote such functions will know that this is partly true: GCs are good, copying is cheap (but arrows that chase large structures to copy them are often very hostile to caches), but actually this is not enough (and compilers almost never don't do the magic they claim). Thus, you either cheat by resorting to non-functional code for free, or you memoize. If you memoize functions, then you only build all temporary structures once and everything becomes cheap (except in memory, but a suitable weakness in memoization can handle this).
Thirdly: if your language does not support light metalinguistic abstraction, this is a serious pain for implementing memoization. Or, to put it another way: you need Lisp macros.
To remember a function, you need to do at least two things:
- You need to control which arguments are keys for memoization - not all functions have only one argument, and not all functions with several arguments should be stored in memory on the first;
- You need to intervene inside the function to disable any self-grabbing optimization that completely destroys memoization.
Although it is so cruel because it is so simple, I will demonstrate it by ridiculing Python.
You might think that decorators are what you need to memoize functions in Python. Indeed, you can write memoizing tools using decorators (and I wrote them). And these are even peculiar works, although they do it mostly by accident.
To begin with, a decorator cannot easily know anything about the function that he decorates. Thus, you can either try memoize based on a tuple of all the arguments to the function, or specify in the decorator which arguments are memoize, or something equally grotty.
Secondly, the decorator receives a function that he decorates as an argument: he cannot quarrel inside. This is actually OK, because Python, as part of its concept of “no concepts invented after 1956,” of course, does not imply that calls to f lexically in the definition of f (and without intermediate bindings) are actually independent calls. But perhaps one day this will happen, and all your memories will now be broken.
So, in short: for a robust memoize function, you need Lisp-style macros. Probably the only imperative languages that are are Lisp.