January 21, 2025
Abstraction and reusability are key principles in software development that offer significant benefits for managing complexity and readability of your code. They promote separation of concerns and modularity, allowing implementation details to be isolated. This enables other developers to reuse code without needing to delve into the specifics of an implementation. It can also help reduce "copypasta", where you copy and paste blocks of code, sometimes with minor modifications, to multiple places, making it extremely difficult to maintain.
A technique to encourage abstraction and reusability is using higher-order functions and passing one function as parameters to another function. In both JavaScript and Python, functions are first-class objects and can therefore be assigned to variables and passed as parameters to other functions. This capability is also available in Automation Scripts for both languages.
Higher-order functions can be seen in many built-in functions such as forEach()
, map()
, filter()
, and reduce()
. These extremely useful functions illustrate the utility of this approach and are the foundation for the functional programming paradigm. Passing functions as parameters can also be used for event handling and callbacks. For example, you might want to allow a script to post progress updates without the script needing to understand the implementation of how the update is handled.
The example below shows how to define two functions, add
and subtract
, that take two parameters and return the result of adding or subtracting the two parameters. The apply
function takes a function as a parameter and calls it with the two values passed to it. The apply
function is then called with the add
and subtract
functions as parameters and the result is logged to the Maximo script logger.
The example is simple to illustrate the concept. In practice, you would likely have more complex functions and use cases.
var add = function addition(a, b) {return a + b;}var subtract = function subtraction(a, b) {return a - b;}function apply(value1, value2, operation){return operation(value1, value2);}// The answer is 3service.log_info("Result of adding 1 and 2 is " + apply(1, 2, add));// The answer is 1service.log_info("Result of subtracting 2 and 1 is " + apply(2, 1, subtract));
The same example can also be implemented in Python. Note however that in Python the function must first be defined and then assigned to a variable, unlike JavaScript where the function can be assigned directly to a variable.
# define the add functiondef addition(a,b):return a + badd = addition# define the subtract functiondef subtraction(a,b):return a - bsubtract = subtractiondef apply(value1, value2, operation):return operation(value1, value2)# The answer is 3service.log_error("Result of adding 1 and 2 is " + str(apply( 1, 2, add)))# The answer is 1service.log_error("Result of subtracting 2 and 1 is " + str(apply(2, 1, subtract)))
Another very useful application of passing functions as parameters is when invoking one Automation Script from another. We covered invoking scripts in a previous post here. In this post we build on that to add a function to the global context and pass it as a parameter to a script named SHARPTREE.LIB
. The SHARPTREE.LIB
script can then call the add
and subtract
functions that were passed as parameters directly, as if those functions were defined within the script itself.
// import the Java HashMapHashMap = Java.type('java.util.HashMap');// define the add functionvar add = function addition(a, b) {return a + b;}// define the subtract functionvar subtract = function subtraction(a, b) {return a - b;}// create a new HashMap to use as the execution contextvar library = new HashMap();// add the standard service to the global contextlibrary.put('service', service);// add the add function to the global contextlibrary.put('add', add);// add the subtract function to the global contextlibrary.put('subtract', subtract);// invoke the library with the contextservice.invokeScript("SHARPTREE.LIB", library);
# import the Java HashMapfrom java.util import HashMap# define the add functiondef addition(a, b):return a + badd = addition# define the subtract functiondef subtraction(a, b):return a - bsubtract = subtraction# create a new HashMap to use as the execution contextlibrary = HashMap()# add the standard service to the global contextlibrary.put('service', service)# add the add function to the global contextlibrary.put('add', add)# add the subtract function to the global contextlibrary.put('subtract', subtract)# invoke the library with the contextservice.invokeScript("SHARPTREE.LIB", library)
An example implementation of the SHARPTREE.LIB
script may look like the following.
Note that the the implementation for
SHARPTREE.LIB
checks that theadd
andsubtract
functions are available in the context before calling them. It is good practice to validate that the expected global variables are available before using them. This is especially important when invoking scripts from multiple launch points or contexts. For example thembo
variable may be available when the script is launched from a object launch point, but not when launched from a direct REST API call.
In JavaScript using the built in typeof
function to check if the add variable is defined in the context and returns undefined
if it is not.
// call the main function to ensure a single entry point to the scriptmain();// the main function provides an entry point to the script so there is a single point of entry and exit, exiting the function also exits the script.function main(){if(typeof add !== 'undefined'){service.log_info("Result of adding 1 and 2 is " + add(1, 2));} else {service.log_error("The add function not found in context");}}
In Python using the built in globals
function returns an array of global variables, which we can use the in
operator to check if the add
function is defined in the context.
def main():if 'add' in globals():service.log_info("Result of adding 1 and 2 is " + str(add(1, 2)))else:service.log_error("The add function not found in context")# call the main function to ensure a single entry point to the scriptmain()
In this post, we demonstrate how to define a function and pass it as a parameter to another function in both JavaScript and Python. We also show how to pass functions as part of the global context when invoking other Automation Scripts. This technique can be used to create reusable and modular code that is easier to maintain and understand. It can also be used to invoke one Automation Script from another and pass functions as parameters to the invoked script. This can be useful when you want to provide a function to another script without needing to define it in the script itself.
If you have any questions, comments or have suggestions for topics you would like to see us cover, please reach out to us at [email protected]