Tuesday, May 7, 2013

Use Functions Luke

JavaScript has been called Lisp with a C-style syntax. It's main building-block is Function. You create functions that call other functions, and also functions that return or take functions as arguments. That is called "support for higher-order functions".

This blog-post is NOT about higher-order functions. Nor is this about the 'module-pattern'. This blog-post is about using functions to encapsulate your "direct" reads and writes.

In JavaScript code written by a novice, you may see something like this:

        myObject[mode] = anotherObject.xyz ;
   // In some other Galaxy:
   if (someObject[someVariable] == something)
   { ...

The problem with the above? You make a "direct write", and you make a "direct read". Why is that bad?

The problem with direct writes is that you can make them from anywhere. If you allow that, it becomes very difficult to locate the place where a specific value is written to a field of a specific object. Who dunnit? Which statement (among ten thousand) wrote that phantom value into my field? It is a problem of JavaScript that you can do that. But you must not give in to the temptation.

With code like above, you can't use your editor's "find" -command either to locate places where the given field is written. That is because field-name CAN be in a variable, as in the example above. But even if it is not, you might find too many places that write SOMETHING into the given field. And you need regular expressions to locate both ".fieldX" and ". fieldX" and ... you get the point.

There's an easy remedy to this maintenance nightmare.
Use Functions, Luke.

 function setit (object, field, value)
 { if (value == 'weird') && (field == 'leftField')
   { debugger
   object[field] = value;

If you never assign a value EXCEPT inside setit(), you can start the debugger whenever you suspect something is written that shouldn't.

If you are a follower of Functional Programming (FP) you know that assignments are BAD. From that perspective the benefit of using  setit() for all writes is that at least you KNOW where all the bad code is. So you can keep an eye on it.

The function setit() can be extended so that it does not allow assignment if the field already has a value. Then you are pretty close at least in spirit to FP.  Another name for "once-only-assignment" is "binding".  Binding is good, (multiple-) assignment is bad.

So is that all there is to it?  Well it's also useful to never READ fields directly. If you code

  var v = someObject [ fname ];  

it becomes difficult to find all places that use data from that specific field of that specific object.

There is no way you can HALT your code every time the value of the field is read. So you can not see when it's used and by whom. That means you can't easily change the value to a different type because you can't find which other places assume it is something else.

It then becomes difficult to change anything without breaking something. And that problem usually only becomes obvious in mid-flight, when trying to escape the death-star.

So what do you do? Use Functions, Luke:

  function getit (object, field)
  { if (field == 'field_of_interest')
    { debugger
      // now we can see whose's asking for this data
    var value =  object[field];
    return value;

This pattern in its slightly different O-O form is often called simply  'Getters and Setters'. The main thing about it is that you must follow it ALWAYS.

If you don't follow it "as a rule" you soon start skipping its use in most places, because direct reads and writes are faster to code.

Then you will have 10,000 places in the code of your hyper-drive that do direct reads and writes.  At that point it is prohibitively expensive to re-factor your engine into maintainable form.  Meaning you can't catch phantom reads and writes. You must surrender to the dark side. Don't let this happen, Luke. Use Functions.

© 2013 Panu Viljamaa


No comments:

Post a Comment