My rule is: It should be obvious by brief inspection (say, 30 seconds or less) that the function's name corresponds to its implementation, assuming that any functions it uses have implementations that correspond to their names.
Practically, this means that my functions are pretty short. Some of them are simple definitions. As a pathological example:
int AdditiveInverse(int x) { return Negate(x); }
If AdditiveInverse and Negate are distinct concepts in my head, and just happen to coincide in the real world. There's nothing wrong with a teeny function if it introduces a new concept.
I've found this rule of thumb is easier to pull off with a pretty high-level programming style. Memory management, separate index passing, etc. hinder the feasibility of this. I find functional languages to be especially amenable to this style: it's an unbreakable guideline to me when I'm writing Haskell, and a loose heuristic when I'm writing C#.