You could think of persistent procedures as 'fire & forget' parts of your application. So you run the procedure and it then takes care of itself.
In Simon Sweetman's example, a procedure is created that contains a while lot of internal procedures, each doing very different things. Instead, you could create a load of separate .p's and run each one individually when required. The disadvantage is that there's an overhead for loading and executing r-code in memory and of course, you have to maintain many different .p files instead of just one.
Persistent procedures really allow you to create Objects. Simon's example is really a library object. Other examples are SmartObjects.
They can also be used to store run-time information. For example, if each routine in a persistent procedure needs to know say, the user ID, then it can store this information in a variable and because the procedure doesn't end, the value will remain intact.
If you adopt the approach of using several .p's run non-persistently, then you would need to pass the User ID as a parameter, which probably means shared variables will be in use somewhere and the whole thing becomes much less elegant.
So what happens when a persistent procedure is run? A persistent procedure usually contains functions, internal procedures and/or triggers. A persistent procedure without any of these things would be of little value. When the .p is run persistently, all code in the PROCEDURE-BLOCK is executed line-by-line regardless of where it physically exists in the source file. By PROCEDURE-BLOCK, I mean any 4GL code that isn't inside a function, internal procedure or trigger block.
For example, if you have 4GL code between lines 1-10 of your procedure. And an internal procedure between lines 11-20. And then more 4GL code between lines 21-30. Then the code between lines 1-10 AND 21-30 will be executed when the procedure starts.
When the procedure is run, all code in the definitions and procedure blocks will be executed from top to bottom. The code in the other blocks is only executed when triggered or when specifically called.
The procedure then exits back to the caller (either after the last 4GL statement in the PROCEDURE-BLOCK or on a RETURN at any point), but instead of being removed from memory as with non-persistent procedures, it remains. All of the triggers remain active, all of the local data (variables) remain in memory, and all of the functions and internal procedure are available to be run.
The main point to note here is that all code in the PROCEDURE-BLOCK can only ever be run once, when the persistent procedure is first started. So although all trigger code, functions and internal procedure can be run multiple times, the code in the PROCEDURE-BLOCK can only be run once per instance of the procedure.
HTH