7

I was working on some code earlier today, when I realized, "Hey! This code would be more concise and semantic if I abstracted the idea of a boolean not out of an anonymous function and into a prototype function..."

Consider a predicate generator:

function equalTo(n) {
    return function(x) {
        return n==x;
    };
}

So you can use it like so:

[1,2,3,4,1,2,3,4].filter(equalTo(2)) == [2,2]

Now, my idea is to make a predicate "inverser":

Function.prototype.not = function() {
    //???
}

So that you can say:

[1,2,3,4,1,2,3,4].filter(equalTo(2).not) == [1,3,4,1,3,4]

My first stab at the implementation was probably very naive:

Function.prototype.not = function () {
    return ! this(arguments);
}

And probably why it didn't work.

How would you implement this function, and why?

I'm just trying to wrap my head around functional ideas, and know JavaScript well enough to know it can be used to do this, but just not how.

4
  • while I approve in general of what you're doing, I feel this is a more OO approach than functional. The functional approach would be like not(equalTo(2)) to create an inverse predicate Commented Jul 21, 2010 at 2:40
  • @Luke: I agree. I come from a very OO background, though, so I guess this just represents my transition from OO to functional programming... Commented Jul 21, 2010 at 3:42
  • I'm exactly the same so I understand completely :) On an unrelated note - make sure you check out clojure and coffeescript (in fact, I think you'll love coffeescript) Commented Jul 21, 2010 at 4:24
  • I will definitely take a look at those. I've seen coffeescript once or twice before, but haven't given it a second thought. Guess I'll give it a closer inspection. Commented Jul 21, 2010 at 4:41

3 Answers 3

8

Your implementation won't work for several reasons:

  • You need to return a function, not a boolean.
  • You should pass the arguments as-is, not wrapped in an array.
  • You should preserve the context (this keyword) that the function would have been called in.

I would implement it like this:

Function.prototype.not = function (context) {
    var func = this;
    return function() { return !func.apply(context || this, arguments); };
}
  • I return an anonymous function (function() { ... })
  • I call apply to call the original function in the current contexts with the actual arguments.
  • (EDIT) Free bonus: I added an optional context parameter which will override this for the callback.
Sign up to request clarification or add additional context in comments.

8 Comments

Very enlightening. Thank you.
And as not is a function, you call it using equalTo(2).not().
It would be better to assign the returned function as the prototype so you can call equalTo.not() instead of equalTo.not()() - e.g. Function.prototype.not = (function (context) { ...})();
@Luke: That won't work, because the context at the call site will be wrong. (When the function in your prototype is called, you won't be able to know which function the property was accessed for) You could do it with Object.defineProperty, though.
@SLaks - 'not' is prototyped to the anonymous function that is returned from equalTo, so mapping equalTo(2).not would work. The problem I think is with my original example thinking the usage would be equalTo.not(2) but it's not, it would be, in a straight call, equalTo(2).not(variableToCheck). The context of the anon function remains the same
|
2

I would probably do it like so (but perhaps with some sort of namespacing):

function not (x) {
  return !x;
}

function id (x) {
  return x;
}

function compose (/*funcs*/) {
  var args = arguments.length
    ? Array.prototype.slice.call (arguments)
    : [id]
    ;
  return function () {
    var val = args [args.length - 1].apply (null, arguments);
    for (var i = args.length - 2; i >= 0; --i) {
      val = args [i] (val);
    }
    return val;
  };
}

[1,2,3,4,1,2,3,4].filter (compose (not, equalTo (2)));

1 Comment

I like this method. It is less OOPish and more flexible.
1

Using your idea:

function equalTo(n) {
    var fn = function(x) {
        return n == x;
    };
    fn.not = function(x) {
        return n != x; // use this for simpler conditions
        return !fn.apply(this, arguments); // use this for more complex logic
    }
    return fn;
}

So your example would work:

[1,2,3,4,1,2,3,4].filter(equalTo(2).not) == [1,3,4,1,3,4]

Edit: You can write a helper function (better name to be found) so not doesn't need to be redefined every time:

function generateFnWithNot(fn) {
    return function () {
        var f = fn.apply(this, arguments);
        f.not = function () {
            return !f.apply(this, arguments);
        }
        return f;
    };
}

So when you're defining your functions, you can define them as normal with the exception of wrapping a generateFnWithNot call:

var equalTo = generateFnWithNot(function (n) {
    return function (x) {
        return n == x;
    };
});

equalTo(5) // resolves to function () { return n == 5; }
equalTo(5).not // resolves to function () { return n != 5; } 

2 Comments

This is an interesting idea, in that not is scoped to equalTo. The only stumbling block is that it would not compose well with other functions, as you would have to re-define not for each one.
I edited my answer to include a helper function that will add not to a generic function, so now not doesn't need to be redefined for each function -- only a function call is required.

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.