Monday, July 7, 2014

A Memory Leak in IE-9

I recently read David Glasser's blog-post "A surprising JavaScript memory leak found at Meteor" about a JavaScript memory-leak in Chrome. That blog-post is about a year old so I wanted to check if things might have improved since then.  I tried a simplified version of his example in IE-9 and surprisingly could reproduce the leak with even fewer statements.

Here's my modified, simplified version of David's example:
 
  var variableOfOuterScope = null;

 function runManyTimes() 

 {var previousValue    = variableOfOuterScope ;
  variableOfOuterScope 
  { bigArray:       new Array (2000000).join('*')
  , aRefToAFunction: function () {}
  };
  // previousValue = null;
  // Un-commenting the above line will fix the leak.
 };
  
 for (var i=0; i < 30 ; i++)
 { runManyTimes () ;  
 }
 // See in Windows Task Manager how the memory used  
 // by IE9 has grown significantly, to about 240 mb
 // after running the code above.


After running the code it seems the browser now uses about 240 mb instead of the 24 mb it used before running it. This is not good, memory is bursting at the seams! Why does it  happen? Why does the memory leak?  I assume it goes something like this:

When you call runManyTimes for the first time it creates a new "context" which the anonymous function inside it holds on to. This context refers to variables that exist outside the anonymous function, so that if it wanted to, it could refer to their values which were in effect at the time runManyTimes exited.  This context refers to the variable 'previousValue' whose value when runManyTimes returns first time is null. But the value of variableOfOuterScope now contains a very big Array. By the time you have called it the 2nd time,  previousValue points to that very big Array and  variableOfOuterScope then contains yet another big Array.

Each time you call runManyTimes  this chain of very big Arrays gets one bigger.  But if you un-comment the fix-line and set previousValue to null, this chain is broken, and  garbage gets collected.

One more thing is required to cause the leak to happen: The  variableOfOuterScope  must contain a REFERENCE to the anonymous function. If you just created a simple inner function without referring to it, there is no leak. Saving the anonymous function to a field in  variableOfOuterScope  means each of its values refers to the anonymous function (created on a specific call  of runManyTimes) which holds the context which refers to the previous value of  variableOfOuterScope and so on. Without that the variableOfOuterScope  would not refer to the function which holds on to the context, which refers to the previous value, which refers to the previous value and so on. But since it does, it now refers to the whole chain of the big arrays, thus consuming the memory.


Ah, but then, I ran the example on Chrome v. 31. There was no memory leak, even without setting previousValue to null!  Chrome is able to prevent the leak on its own. IE-9 is not. Maybe Chrome realizes that the anonymous function is not referring to any variables, so it need not create a "context" for it.

The moral of this story then is: Beware of memory-leaks in  JavaScript, when creating functions within functions.  It is fairly easy to detect the leaks by observing the amount of memory your browser consumes, when running your application. Don't worry too much about what causes them exactly, because results might differ on different browsers. Try to reset variables whose values you no longer need to null, and that may fix it.  Help the garbage-collector a bit!


 © 2014 Panu Viljamaa. All rights reserved

Tuesday, July 1, 2014

Partial Partial Application

"Partial Application" (PA) means applying a function to only part of its arguments, and getting back a function which can take in the remaining ones. This post is about it and what I like to call "Partial Partial Application" -  meaning the resulting function can do partial application on its own, as well.

In a previous blog-post I wrote about "currying", a related concept. I posted that currying is the process of implementing a multi-argument function as a combination of single-argument functions, and calls to them. I'll discuss the difference and relationship of Partial Application and Currying, briefly.

Partial Application is something your program can do. "Currying" is something you, or your compiler or interpreter does. So PA seems like a  more straightforward concept to implement. And yes, there is an accompanying code-file that does it for you.

Let's start by asking:  If we had an implementation of the function 'applyPartially()' or applyP() for short, how would we use it? Let's write some test-cases! Ah, this is Test-Driven-Programming, we write the test-cases first?  Well maybe something like that. But let's start by writing the function applyP(), a 'dummy' version of it first. That dummy version says something about what it should do when it grows up, what kind of arguments it expects etc.

  function applyP (baseFunction, a, b, c, d, e)
  { 
   /**  ... NOT READY   
    Return a function that is like the argument 
    'aFunction' except that its leading arguments 
    will be "fixed" to be those given by the rest 
    of MY arguments. So, my result is a function
    which will (typically) take FEWER arguments 
    than my 1st argument, 'aFunction'.  
    */
   ...
  }

The 1st argument  of applyP( ) is called  baseFunction because the result of applyP() will be  derived  from it.

Now let's write one test for it:

 function f2(x,y)
 {return x + y.
 } 
 var f2b     =  applyP (f2, 1);
 ok (f2b (2) == 3);

Above we partially apply f2 with '1' as the fixed value of its first argument, and get a function f2b as the result.  Then we call f2b() passing only one argument '2' to it. Then verify that the result is 3 with our simple test-utility function 'ok()'. There, almost done. Should we go further? No, unless we can answer the following question:

What is it good for? Absolutely nothing? No. Partial Application is good whenever some arguments of a function come from a small fixed set. That usually means you will be calling the function many times with the same values from that fixed set. In such a case it makes sense to partially apply the function to those frequently used values. That will give you new functions that are simpler to call and easier to understand. They will be simpler because they take fewer arguments. They will be easier to understand because they can be given more meaningful names.

A typical example is any function that takes a boolean as one of its arguments, indicating a caller-preference of some kind.  Example:

  function runTest (haltOnError, testCase)
  { /* Run the test and if 1st argument is true
       and test-case fails, throw an error. If
       1st arg is false just return a boolean
       telling whether the test succeeded or not.
     */
    ...
  }

Now if you call runTest() many times,  sometimes with true,  sometime with false,  it might be a good idea to refactor it into two single-argument functions, by using  Partial Application:

   var runTestHalt   =  applyP (true,  aTestCase);
   var runTestNoHalt =  applyP (false, aTestCase);
   ...
   runTestHalt (myTestCase);
   //   runTestNoHalt (myTestCase);

The new partially applied functions are easier to understand because their names can express more about their semantics, because the semantics is simply simpler.There are fewer or no IF-THEN conditions you need to understand.  If you instead use the original test-function, your code would look like this:

   runTest (false, myTestCase) ;

Looking at the runTest() -call above it's not clear at all what is the meaning of the first argument. We can see it is false, but what does that MEAN?  It's hard to remember, takes some effort to look it up, and easy to pass a wrong value to it. Maybe you changed its meaning, and forgot to change some places where it was already called. When the error hits the fan in production, start debugging.

The First Rule of O-O Programmers:  Beware of IF-THEN !

In the accompanying code-file I provide an implementation of ClassCloud.applyP() which does partial-application for you. More than that, it returns functions that can successively partially apply themselves, to further arguments. Here's an excerpt from the unit-tests inside the code-file that show how it can be used:

   function f2 (x,y){return x + y}

   ok (applyP(f2, 1, 2)          == 3);
   ok (applyP(f2, 1   ) (2)      == 3);

   ok (applyP(f2      ) (1) (2)  == 3);
   ok (applyP(f2      ) (1,2)    == 3);


The last two tests above are interesting because they show that we can do partial application by providing ZERO arguments to fix, even though the function really needs more to calculate its value. Why would we do partial application with zero fixed arguments?  The last two lines  give us the clue: applyP(f2) returns a function that can be called exactly like its argument-function f2:
 
   ok ( applyP (f2) (1, 2) === f2 (1, 2) );

So it can do the same thing. But, it can also do MORE:  It can apply  ITS arguments partially, in separate calls as shown above and here again, still returning the same result as f2:

   ok ( applyP (f2) (1)(2) === f2 (1, 2));

We could say that applyP(f2) is not just a different version of f2(), it is a BETTER version of it. It can do everything  f2()does, and then some:  It can take its arguments incrementally.

applyP() as shown by above examples does partial application. But depending on how many arguments you give it, there may be more  partial application left you can do with its result.  Hence the title of this blog-post: Partial Partial Application. The results of applyP() can take care of the further rounds of partial application themselves, and they can do that in multiple stages as well - partially. applyP() can do partial application partially, or fully.


So how is Partial Application related to currying?  My previous blog-post was about currying. In that post I showed  how a function for calculating  max. of two numbers could be implemented in the "curried form" as two single-argument functions, one inside the other, the inner function returned as the result of the outer one:

  function max(a)
  { return maxB;
    function maxB (b)
    { return a < b ? b : a;
    }
  }

Note that the above function is not doing "currying". It is the result of you (or me) having done it. Making your compiler do it automatically is possible but a lot of work; it basically means implementing your own PL. Some PLs do it for you.  Whereas creating a function like applyP() should be within the realm of the practical, as shown by the accompanying, rather small file of JavaScript.

If you look at that code you will see that applyP()is mostly implemented by applyPBasic()which contains AND RETURNS the inner function newFunk(). That is basically the curried form shown for max() above. Currying is the way to implement Partial Application!

There's one more thing to know about ClassCloud.applyP():  It relies on the number of declared arguments of the base-function. If you use it with functions that rely on varying number of arguments, your results might vary (pun intended!). If you call it with more than the declared number of arguments you get an error, which is useful for error-checking. It checks that your assumptions about how many arguments a function takes are correct.  

You can copy the MIT-licensed implementation of applyP() including its tests from http://panuviljamaablog.blogspot.com/2014/07/classcloudapplypjs.html .


 © 2014 Panu Viljamaa. All rights reserved


ClassCloud.applyP.js

/***********************************************************

Implements the JavaScript -function 'ClassCloud.applyP()' 
which creates "Partially Applied" versions of any function, 
to "fix" part of their arguments. See the tests -section
below for how to use applyP(). 
URL: http://panuviljamaablog.blogspot.com/2014/07/classcloudapplypjs.html 
*********************************************************** 
   
This software is made available under
"The MIT License", http://mit-license.org/.

Copyright © 2014 Panu Viljamaa, ClassCloud LLC

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */


function ClassCloud  ()
{ /* This is the only global created by the loading 
     of this js-file. After loading this file you can 
     get a reference to the applyP() -function like this:
     var applyP = ClassCloud.applyP;
   */
}

(function anonymous  ()
{ "use strict" ;
  var _PREVIOUSLY_GIVEN_ARGS;
  var _MISSING_ARGS_L       ;
  ClassCloud . applyP = applyP;
  return;


  function applyP (aFunction, a,b,c,d,e)
  {
    _PREVIOUSLY_GIVEN_ARGS  = [];
    var argsArray = [].slice.call(arguments);
    var fargs       = argsArray.slice(1);
    var neededArgsL = numbOfArgs(aFunction);
   
    if (fargs.length > neededArgsL)
    { var emsg =
        "Too many args given to ClassCloud.applyP() "
        + fargs.length  + ". "
        + "The base-function only expects " 
        + neededArgsL   + ": \r\n"
        + aFunction;
        throw emsg;
    }
   
    var result      
     = applyPBasic (aFunction, fargs, neededArgsL);
    return result;
    
      /**
       Return the 1st argument partially applied
       to the rest of the arguments.  
       */
  }

   function numbOfArgs(aFunction)
   { var s  = aFunction.toString();
     var p1 = s .split(/\)/) [0];
     var p2 = p1.split(/\(/) [1];
     if (p2.match(/^\s*$/) )
     { return 0;  // IE7 fix
     }
     var argNames = p2.split(/,/);
     return argNames.length;
   }


  function applyPBasic (topFunk, fargsArray, missingArgsLength  )
  {/*  
      'missingArgsLength' does NOT yet include the ones that
      may now be given in fargsArray
    */
    var knownArgs           = _PREVIOUSLY_GIVEN_ARGS;
    var argsKnownNow        =  knownArgs.concat(fargsArray);
    _PREVIOUSLY_GIVEN_ARGS  = argsKnownNow;
    _MISSING_ARGS_L         
       = missingArgsLength - fargsArray.length;
   
    var mislen = _MISSING_ARGS_L
    if (mislen == 0)
    { var result = topFunk.apply(this, argsKnownNow);
      return result;
    }
    return newFunk;

    function newFunk (y,z)
    { var result;
      var myArgsArray = [].slice.call (arguments);
      var previouslyGivenArgs = _PREVIOUSLY_GIVEN_ARGS;
      var argsKnownNow 
        = previouslyGivenArgs.concat(myArgsArray);
       
      var weKnowAllArgs
        = (_MISSING_ARGS_L - myArgsArray.length) <= 0;

      if ( weKnowAllArgs )
      { result = topFunk.apply(this, argsKnownNow );
        return result;
      }
      var nextLevelF
         = applyPBasic (topFunk, myArgsArray, _MISSING_ARGS_L);  
      return nextLevelF;
    }
  }
}) ();



// ====================  TESTS: ======================


(function anonymous2 ()
{ "use strict" ;
  if (false)   
  { /*  Change false above to true, to skip
        running these tests, which makes sense
        if you include this in the production
        version of your software. You could
        of course remove these tests alltogether.
        You can modify this source-code except
        as restricted by the MIT license given
        at the top.
    */
   return
  }

  var applyP = ClassCloud.applyP;

  testF2();  // Simple but interesting
  testF3();  // N arguments.
  testF1();  // Trivial case
  testF0();  // Pathological case

 return;
  
  
  function testF2 ()
  { 
     ok (applyP (f2, 1, 2)          == 3);
     ok (applyP (f2, 1   ) (2)      == 3);
     ok (applyP (f2      ) (1) (2)  == 3);
     ok (applyP (f2      ) (1,2)    == 3);
     /* 
        f2() sums up its TWO arguments.
        The last 2 cases above are interesting because 
        they show that we can do partial application 
        without passing in ANY arguments. See the 
        blogpost for more explanation. 
        */
     function f2 (x,y){ return x + y}
  }

  function testF3 ()
  {
     ok(applyP (f3      ) (1)(2)(3) == 6); // a)
     ok(applyP (f3      )   (1,2,3) == 6); // b)
     
     ok(applyP (f3, 1   )    (2)(3) == 6);
     ok(applyP (f3, 1   )     (2,3) == 6);
     
     ok(applyP (f3, 1, 2)       (3) == 6);
     ok(applyP (f3, 1, 2, 3)        == 6);  // c)
     /*
       Above shows that there are 3 ways you can  
       inject the arguments into the calculation: 
       a) You can give them ONE BY ONE to the result 
          of applyP().
       b) You can give one or more of them in a SINGLE
          call to the result of applyP().
       c) You  can give them ALL when calling applyP(). 
      */
     return;
      
     function f3 (x,y,z)   { return x + y + z }
  }
  
  
  function testF1 ()
  {
   var f1P =  ClassCloud.applyP (f1);
   f1P ()  ;  
   ok( f1P()()()().constructor == Function)  ;
   // As long as you call f1P with TOO FEW arguments
   // you will be getting a curried function back.
   
   ok (applyP (f1) (55) == 55)  ;

   var emsg = null;
   try 
   { applyP (f1, 1, 2)  ;
     /* Above fails because you are trying to
        apply more arguments than the base
        function has declared to itself. 
        Calling it with too many args would
        be of no use so we treat it as error.
    */
   } catch (e) 
     { emsg = e;
     }
   ok (emsg != null);  
   return; 

   function f1 (x)   { return x}     
  } // end testF1.


  function testF0 ()
  {  
   ok (ClassCloud.applyP (f0) == 123);
       
   var emsg = null;
   try 
   { ClassCloud.applyP (f0)()   ;
     /* Above fails because ClassCloud.applyP(f0)
        return 123, which is not a function.
      */
   } catch (e) 
     { emsg = e;
     }
   ok (emsg != null);  
   
   function f0 ( )   { return  123; }
     
  } // end testF0
  
  function ok (bool)
  { if (! bool)
    { throw "ERROR. ok(bool): bool == " + bool ;
    }
    /**
     Our simplest possible testing-utility
     */
  }
}) ();