/***********************************************************
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
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
of this js-file. After loading this file you can
get a reference to the applyP() -function like this:
var applyP = ClassCloud.applyP;
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.
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;
= 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
return
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.
*/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.
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.
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
*/
/**
Our simplest possible testing-utility
*/
}
}) ();
No comments:
Post a Comment