The purpose of this handout is to clarify the meaning of ``modularity'' and to describe how programs should be written so that they are properly modular.
Modularity has to do with the organization of the code, not with its functioning. It's possible to have a program that works entirely correctly but the code is a mess, with jumps and calls scattered randomly, and so forth. Code like that is hard to maintain, which means that if the program ever needs to be modified or improved, it's very difficult to do that.
The idea is that programs should be built out of modules, each of which has a job to do or a role to play. The job should be easily described in a simple way, such as ``reads in filename.'' or ``computes transpose of matrix'' or ``finds element in list'' and so forth. A job should never be ``miscellaneous'' or an odd combination of tasks, such as ``finds index of element and sorts the remainder of the array.'' This judgment is ultimately aesthetic, like beauty or elegance, but it can be learned.
Programmers often picture a module as a box. The program consists of boxes with arrows between them, indicating when one module calls another module. A module can be built, recursively, with sub-modules connected among themselves.
Modules make a program easier to understand because it can be understood in parts, each of which makes sense by itself. Therefore, no one ever needs to hold the whole program in mind at once, but can concentrate on a just a few modules. However, if there are many connections between two modules, it is hard to understand them in isolation. Therefore, you should minimize the connections between modules. If you find that two modules seem to be too intertwined, perhaps you should re-think the programs organization, either combining the two modules, breaking the combination down in a different way, or breaking each of them up into smaller modules that interact less.
In terms of a program's code, a module can be (1) a function, (2) an object, (3) a collection of functions, (4) a collection of functions and objects, (5) large files filled with code, (6) even bigger stuff. Obviously, the larger modules will do bigger tasks, and are probably broken down into sub-modules (which are broken down into sub-submodules, and so forth). Don't fixate on any one programming construct as a module: a module is some collection of code that does a specific, coherent task. Consider an artificial intelligence system that has modules like ``reason,'' ``speak,'' ``hear,'' ``see,'' and so forth. Each of those modules could be gargantuan amounts of code. The modularity of the program is still helpful, though, because if there's a bug in the vision code, we can easily skip all the other modules and concentrate on the one.
In most cases, the interface between two modules consists of either a function call or a message sent to an object (which is just a fancy function call). The caller is asking the callee to do some task, which is in the callee's job description. When the job is done, control returns to the caller, possibly along with a return value.
In MIPS assembly language programming, a call is done with jal and a return is done with jr $ra. We will not have messages and objects in MIPS assembly language, so you only need to worry about function calls. Therefore, you should break up your program into functions, and the functions should only be connected by jal instructions. Any targe of a jump or branch instruction should be inside the function that does the jump or branch; you should never jump from one function (module) to another.