John Caruso
Kilo Guru
This is the first in hopefully a series of articles on incorporating useful functional programming techniques while writing JavaScript in ServiceNow.

How many times have you seen code like the following?
var grIncident = new GlideRecord('incident');
grIncident.addQuery('active=true^state=1');
grIncident.query();
while (grIncident.next()) {
    // do some processing with the GlideRecord
    gs.info('processing ' + grIncident.getDisplayValue());
}

This boilerplate code is so common in the platform, you probably use a code snippet or macro to type everything for you, then go back and edit as needed.

Any time you see the same code over and over, an alarm should go off in your head. You may have heard the about the DRY (Don't Repeat Yourself) principle. Why is it then, that we as ServiceNow developers violate this design principle virtually everyday when we repeat this boilerplate code over and over again?

If you use such a code snippet, what are the things you end up going back to edit? Most likely they include the table name, query string and the processing to be done.
 
This gives us a hint to what things are fixed and what things change. In software design, a best practice is to separate the things that change from the things that remain the same. An example of this is using system properties to enable changes to code behavior without actually changing the code.

Can we write the GlideRecord query with looping logic in such a way that we separate the things we change each time from the boilerplate stuff that remains same?

Yes we can, and it is the first step we'll take toward using a functional approach for our query logic. Here's a helper function named query that takes three parameters - the very three things we change after using a typical GlideRecord query code snippet:
function query(table, query, fn) {
    var gr = new GlideRecord(table);
    gr.addQuery(query);
    gr.query();
    while (gr.next()) {
        fn(gr);
    }
}​
Here's an example of using our query helper to process new incidents:
function processNewIncident(grIncident) {
    gs.info('processing ' + grIncident.getDisplayValue());
    // do some processing with the GlideRecord
}

query('incident', 'active=true^state=1', processNewIncident);
Here's an example of re-using our helper function for something completely different:
function processClosedHighPriorityIncident(grIncident) {
    gs.info('processing ' + grIncident.getDisplayValue());
}

query('incident', 
    'active=false' + 
    '^ priority=1 ^OR priority=2', 
    processClosedHighPriorityIncident);

No more repeating boilerplate code, and by encapsulating our processing of an individual GlideRecord into a separate function, separating the processing concern from the data retrieval concern (another best practice - Separation of Concerns or SoC) our code becomes more readable and maintainable.

Comments
Javier Arroyo
Mega Guru

Hi John,

I looked for a way to PM you but, wasn't successful. Hope posting here is okay with you.

I've done Functional programming in SNow for quite a bit. This is inclusive of monads, functors, applicatives. I would love to try to find time to collaborate with you to see if better programming practices can brought into SNow. Seeing that you are trying to also tackle the same ideas as I, perhaps we can learn from one another even if it's here and there as time permits.


On to the post:

While functions are being passed around, the code is imperative. To move into functional programming, the code would have to be modified in various places, something along the lines of:

With Monads

var openIncidents = Maybe(Incident.All_ACTIVE)
    .map(makeDB().retrieve)
    .map(composeWhile(pipe([getValues(['number','short_description'])])));

With pipe

var openIncidents = pipe([makeDB().retrieve, composeWhile(pipe([getIds]))])(Incident.ALL_OPEN);

The code doesn't have to be point-free; however, the idea is that in functional programming function arity is nearly always singular, as opposed to varadic arity because those don't compose. More times than not functions are pure (no side effects), and there would be curried functions to separate such things as getDisplayValue from the value it extracts and how it does it, as to allow function composition. Also, all functions must always return a value. 

The function query in your example would be turned into a factory and its while statement separated from the boiler plate of the GlideRecord, introducing the single responsibility principle. This leads to greater flexibility in how processing each record is done and when.

Calling gs.info within another function, while the point of the example understood, is not a functional technique. processClosedHighPriorityIncident would instead return grIncident.getDisplayValue and a trace function created to print the output.

For example, in the second sample I've provided, there would be an additional call to gs.debug to handle out put.

var openIncidents = pipe([makeDB().retrieve, composeWhile(pipe([getIds])),gs.debug])(Incident.ALL_OPEN);

With the monad, it would look like so:

Which would then used to compose functions.

var items = Maybe(Incident.All_ACTIVE)
    .map(makeDB().retrieve)
    .map(composeWhile(pipe([getValues(['number','short_description'])])))
    .map(gs.debug);

 A functional version of procrssClosedHighPriorityIncident would be more similar to:

var getValueUsingGlideFunction (functionName) {
   return function getFromRecord (record) {
      return record[functionName]();
   }
}

Ultimately, the goal is to compose processes by stringing together functions, rather than introducing higher order varadic functions which still abide by imperative techniques.

Nevertheless, I've enjoyed what you've done and thank you for sharing! I wish there were more posts like yours.

John Caruso
Kilo Guru

Javier: Takes us for a ride in his 2019 Tesla Model S, engages Ludicrous mode, and accelerates 0-60 in 2.4 seconds.
John: Takes us for a leisurely ride around the park in a horse drawn carriage.

My apologies if I'm not moving fast enough for you, but this is the very first in a very long series of articles that based on the 8 month span between Part 1 and Part 2, will end some time after George R. R. Martin completes his A Song of Ice and Fire series.

Referencing things monads, functors, applicatives, pipe, arity, variadic, point-free, pure, composition, etc., at this point in the series (if ever?) is more likely to scare away what little audience I have.

That said, I appreciate the feedback! Regarding collaborating to improve programming practices on the platform, I'm afraid posts like this one the community will have to suffice.

Javier Arroyo
Mega Guru

haha.

I'm so sorry. just got excited that someone else is doing what I'm doing. then feared the OOP techniques of the post would create a confusion for newer programmers into thinking it's functional, so I went full steam ahead.

I'll stay tuned to your future posts, and if you don't mind the tesla, add my two cents. That's enough of a collaboration for me 🙂

Version history
Last update:
‎02-03-2019 01:11 PM
Updated by: