|On this page…|
You can define utility functions inside a procedure. For example, define the utility function, helper, inside the procedure f:
f := proc(arguments) local helper, ...; begin helper := proc(...) begin ... end: ... code using helper(...) ... end:
The helper function is not visible or accessible outside f. Your users cannot see the helper function, and therefore, they do not rely on a particular implementation of this procedure. You can change its implementation without breaking their code. At the same time, helper can access and modify arguments of f.
The major disadvantage of this approach is that your test files cannot access helper directly. Since it is typically recommended to start testing at the smallest possible building blocks, this is a real disadvantage. Nevertheless, for many tasks the benefits of this approach prevail over this disadvantage, especially if the utility function must be able to modify the arguments of the calling function.
You can define utility functions outside a procedure. For example, define the utility function, helper, in the function environment f:
f := funcenv( proc(arguments) local ...; begin ... code using f::helper(...) ... end): f::helper := proc(...) begin ... end:
This approach does not require you to define the utility function in the function environment of the procedure that uses it. Defining a utility function in the function environment only helps you clarify to people reading your code that you intend to call f::helper primarily or solely within f. If you later decide to use f::helper in another procedure, you can move the utility function to a more generic utility library. Again, this recommendation only helps you improve readability of your code.
Defining utility functions outside the procedure that uses them does not hide utility functions. Therefore, this approach lets you:
Test utility functions directly.
Define a utility function and a function that uses it in different source files.
Use the same utility function for different procedures.
Defining utility functions outside the procedure that uses them has the following disadvantages:
Your users can access utility functions. If they rely on a particular implementation of the utility function, changing that implementation might affect their code.
The utility function cannot access local variables of the procedure that uses that utility function. The workaround is to pass these local variables as arguments to the utility function.
The utility function does not have privileged access to the arguments of the procedure that uses that utility function.
Defining the utility function far from the code line where you call it reduces readability of the code.
Be careful when defining utility functions in slots of a function environment because MuPAD® uses these slots for overloading. Do not define utility functions with such names as f::print, f::diff, f::evaluate, or f::simplify unless you want to use these utility functions for overloading.
You can define a utility function and all procedures that use it inside one procedure. In this case, you must also define the utility function as a local variable of that outer procedure. The outer procedure can be anonymous. For example, create the anonymous procedure that has a local variable helper and includes the utility function helper and two other procedures, f and g, that use the utility function:
proc() local helper; option escape; begin helper := proc(...) ... end: f := proc(arguments) local ...; begin ... code using helper(...) ... end: g := proc(arguments) local ...; begin ... code using helper(...) ... end: end():
If you define a utility function in a closure, that function is inaccessible to any external code. Your users cannot see and, therefore, rely on a particular implementation of that utility function. Changing it will not break their code. At the same time, this approach lets you create more than one procedure that can access the utility function. In the example, both f and g can access helper.
The disadvantage of this approach is that the helper function cannot access the local variables of the procedures that use it. To overcome this limitation, you can use the context function or shared static variables.
Note: Using context or shared static variables to make local variables of the calling procedure accessible for the utility function is not recommended.
Using context to overcome this limitation typically leads to unreadable and difficult to maintain code. The problems with shared static variables resemble the problems with global variables, especially for recursive calls. The helper procedure can access and modify such variables, but all other procedures inside the same outer procedure can access and modify them too.