1

Here's my compose function, as a polyfill

Function.prototype.compose = function(prevFunc) {
    var nextFunc = this;
    return function() {
        return  nextFunc.call(this, prevFunc.apply(this,arguments));
    }
}

These work:

function function1(a){return a + ' do function1 ';}
function function2(b){return b + ' do function2 ';}
function function3(c){return c + ' do function3 ';}
var myFunction = alert(function1).compose(function2).compose(function3);
myFunction('do');

var roundedSqrt = Math.round.compose(Math.sqrt)
roundedSqrt(6);

var squaredDate = alert.compose(roundedSqrt).compose(Date.parse)
quaredDate("January 1, 2014");

But this does not work!

var d = new Date();
var alertMonth = alert.compose(getMonth); <-- 
alertMonth(d);                   ^^^^

Error throws error "Uncaught ReferenceError: getMonth is not defined" in google chrome.

Now, if I try either of these instead:

var d = new Date();
function pluckMonth(dateObject) {return dateObject.getMonth();}
var alertMonth = alert.compose(pluckMonth);
var alertMonth2 = alert.compose(function(d){return d.getMonth()});
alertMonth(d);
alertMonth2(d);

They work.

Ok, so, why is that? I don't want to write extra functions, I want it to just work. The compose function uses the apply utility and just uses this for the thisArg, so it should work for object members as well as stand-alone functions, right??

i.e., these are equivalent

this.method()
method.call.apply(this)

jsFiddle: http://jsfiddle.net/kohq7zub/3/

7
  • 1
    getMonth isn't a function, it's a property of Date.prototype. Commented Nov 10, 2014 at 0:41
  • Try compose(Date.prototype.getMonth). Commented Nov 10, 2014 at 0:42
  • I have, I've tried all those little tricks. I've provided a jsfiddle if you want to try yourself: jsfiddle.net/kohq7zub/3 Commented Nov 10, 2014 at 0:46
  • 1
    Yeah, I tried that myself, it didn't work. The problem is that this isn't propagated through the composition properly. I'm not sure that you can treat normal functions and methods equivalently like this. Commented Nov 10, 2014 at 0:47
  • 1
    like this? jsfiddle.net/kohq7zub/4 Commented Nov 10, 2014 at 0:52

1 Answer 1

3

If it comes to prototyping the Function object a whole bunch of libraries really do not acknowledge functions as methods.

The last entry in such arguments signatures always should be a target object, a method then can act upon. It solves the binding within the implementation; Thus avoiding to be forced later on having to use bind again and again as sole solution.

The given example slightly needs to be changed (jsFiddle) to ...

Function.prototype.compose = function(prevFunc, target) {
  var nextFunc = this;
  return function() {
    return nextFunc.call(this, prevFunc.apply(target, arguments));
  };
};

const d = (new Date);
let alertMonth;

// both variants do work.

alertMonth = alert.compose(d.getMonth, d);
alertMonth();

alertMonth = alert.compose(Date.prototype.getMonth, d);
alertMonth();

Within a next code iteration the above provided code then might result in something similar to the following implementation ...

const protoGetMonth = Date.prototype.getMonth;
let logMonth;

logMonth = ((month) => console.log(month))
  .compose(protoGetMonth, (new Date));

logMonth();

logMonth = console.log
  .compose(protoGetMonth, (new Date))
  .bind(console);

logMonth();

let alertMonth = alert.compose(protoGetMonth, (new Date));
alertMonth();
.as-console-wrapper { min-height: 100%!important; top: 0; }
<script>
  (function (function_prototype) {
    var
      isFunction = (function (TYPEOF_FUNCTION_TYPE) {
        return function (type) {
          return (
               (typeof type == TYPEOF_FUNCTION_TYPE)
            && (typeof type.call == TYPEOF_FUNCTION_TYPE)
            && (typeof type.apply == TYPEOF_FUNCTION_TYPE)
          );
        }
      }(typeof function_prototype)),

      getSanitizedTarget = function (target) {
        return (target == null) ? null : target;
      }
    ;
    function_prototype.compose = function (previous, target) { // compose
      var
        previousTarget = getSanitizedTarget(target),
        proceed = this
      ;
      return (isFunction(previous) && isFunction(proceed) && function () {

        return proceed.call(this, previous.apply(previousTarget, arguments));

      }) || proceed;
    };
  }(Function.prototype));
</script>

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.