Editor's Note: Start by reading Part 1 of this series if you haven't already.
Procedures are a way of tidying up program structure, but they are also very similar, conceptually, to mathematical functions. The difference between functions and procedures relates to state.
A mathematical function has no concept of global state, while a procedure does (and can reference global program state as well as its own).
If you add restrictions to procedures—forcing them to behave as functions—you can then use mathematical reasoning to prove that aspects of your program are correct.
This is the foundation for languages such as Ocaml and Haskell. Haskell, for example, is a pure functional language—everything is a function. Haskell has no global state, so the result of a function depends solely on its input, as with a mathematical function.
This has a couple of interesting side effects from the perspective of efficiency. The first is that execution order becomes less important. If you have already computed the arguments to a function, you can compute the result whenever you want.
If you have computed the arguments to two functions, you can run them both in parallel because no global state means no side effects.
You also get the idea of lazy evaluation. If you don’t use the return value of a function, you don’t need to bother computing it.
This has been used to produce some simple programs that run on very large data sets and only bother loading the parts that are accessed. The program is written as though it performs a computation on the whole dataset, but only results that are used are executed, so only data needed to compute them is loaded.