Notes about encapsulation/modularity

It takes some experience to decide how to write modular code and how to encapsulate code fragments as procedures and functions. Briefly, the guiding principles are as follows. Any fragment of code that is reasonably independent of other fragments is written as a separate module. This facilitates separate development, testing and upgrading of this module (compared to the rest of the code).

A piece of code that is required over and over is encapsulated as a procedure or function. An important design decision involves placing the procedure. Should one make the procedure a stand-alone fragment (globally visible) or should it be defined (and used) locally within another procedure? The answer to this depends on where the procedure/function is likely to be used. Again, a rule of thumb is to define a pocedure in the most limited scope in which it is meant to be used. So, if a procedure is only going to be used by a single other procedure, it maskes sense for the former to be defined locally within the latter. For example, the following code to compute the set of digits used to write a number the function numdig, and the procedure ithdig could both have been written inside digset.

numdig := n-> 1+trunc(log10(n));

ithdig := proc (n::integer, i::integer) 
return iquo(irem(n, 10^i), 10^(i-1)) 
end proc;

digset := proc (n::integer) 
local m; 
m := abs(n); 
{seq(ithdig(m, i), i = 1 .. numdig(m))} 
end proc;
That is, the following code has the same functionality.
digset := proc (n::integer) 
local m, numdig, ithdig; 
numdig := n->  1+trunc(log10(n));
ithdig := proc (n::integer, i::integer) 
return iquo(irem(n, 10^i), 10^(i-1)) 
end proc; 
m := abs(n); 
{seq(ithdig(m, i), i = 1 .. numdig(m))} 
end proc