3

I am learning function currying in JavaScript by reading online and writing some simple code. I got following example in online article

    function toArray(obj) {
        return Array.prototype.slice.call(obj);
    }

    Function.prototype.curry = function() {
        if (arguments.length<1) {
            return this; //nothing to curry with - return function
        }
        var __method = this;
        var args = toArray(arguments);
        return function() {
            return __method.apply(this, args.concat(toArray(arguments)));
        }
    }

    var add = function(a,b) {
        return a + b;
    }

    var addTen = add.curry(10); //create function that returns 10 + argument
    alert(addTen(20)); //alerts 30 correctly

Then I tried to try it on the method of an instantiated function. So I tried following. But it gave me error "Unable to get property 'prototype' of undefined or null reference" on the second last line. I know this error is nothing to do with currying but I am messing up with some basics of JS functions concept. So where I am going wrong.

    function Person()
    {
        this.age = 15;
    }

    Person.ageAfter = function (years) {
        return this.age + years;
    }

    var personObj = new Person();
    var ageAfterFiveYears = personObj.ageAfter.prototype.curry(5);  //**Error**
    alert(ageAfterFiveYears());
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
Mahesha999
  • 22,693
  • 29
  • 116
  • 189
  • 2
    `personObj` does not have a property `ageAfter`. Only `Person` has. I think you meant to assign `ageAfter` to `Person.prototype.ageAfter`, so that each `Person` instance inherits that function. – Felix Kling Mar 24 '13 at 20:20
  • @FelixKling I read that as a transcription error but you may be right that that's the immediate problem. – Pointy Mar 24 '13 at 20:22
  • @Pointy: It's one of the problems I presume. The mentioned error message *""Unable to get property 'prototype' of undefined or null reference"* supports this. – Felix Kling Mar 24 '13 at 20:29
  • It's one of the problems I mentioned. – Alnitak Mar 24 '13 at 20:32
  • 2
    I'd just like to mention, that's not proper currying; it's partial application. See: http://stackoverflow.com/questions/218025/what-is-the-difference-between-currying-and-partial-application – fncomp Mar 24 '13 at 21:05

3 Answers3

4

You shouldn't include the prototype in your call:

var ageAfterFiveYears = personObj.ageAfter.curry(5);

The "ageAfter" property has a function for its value, so that function will have access to your "curry" function just like the function in your first example.

When you call it on the prototype, the value of this inside your function will be the prototype object, not the "ageAfter" function.

Also as a comment points out, you need to put "ageAfter" on the prototype:

Person.prototype.ageAfter = function(years) { ... }

edit — the binding issue that Alnitak points out is also important. (When I say "important", what I mean is "necessary to make your code work".)

Pointy
  • 405,095
  • 59
  • 585
  • 614
4

You have two issues:

  1. The ageAfter function is not an instance method - you've added it to the "class" (i.e. it's kind of like a static method). It should be added to the prototype.

  2. When you curry the function you lose your object's context, so you either need to rebind the context

e.g:

var ageAfterFiveYears = Person.prototype.ageAfter.curry(5).bind(personObj);

giving you a function that only works on the current instance, or better yet, as @Pointy suggests in the comments, you should just put the curried function back onto the prototype:

Person.prototype.ageAfterFiveYears = Person.prototype.ageAfter.curry(5);

which then adds the .ageAfterFiveYears method to every Person object.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • 2
    Alternatively, the result of calling "curry" could be added as a property of the instance or the "Person" prototype, and then called via the instance just like "ageAfter" is called. – Pointy Mar 24 '13 at 20:25
1

Curry will not work for your use case. You need to bind the context so this is your personObj.

function Person() {
    this.age = 15;
}

Person.prototype.ageAfter = function (years) {
    return this.age + years;
}

var personObj = new Person();
var ageAfterFiveYears = personObj.ageAfter.bind(personObj, 5);
alert(ageAfterFiveYears());
generalhenry
  • 17,227
  • 4
  • 48
  • 63
  • note that although this works, it's probably no use to the OP since it doesn't actually use his `.curry` function. – Alnitak Mar 24 '13 at 20:33
  • yeah I know this bind() approach is also function currying, I tried this earlier and it worked but just wanted to explore other ways – Mahesha999 Mar 25 '13 at 12:43