3

desired functionality:

mult(3);
 //(x) => 3 * mult(x)
mult(3)(4);
 //(x) => 3 * (4 * mult(x))
mult(3)(4)();
//12

attempt:

function mult(x){
    if(x === undefined){
        return 1;
    }else{
        return (y => x * mult(y));
    }
}

result:

mult(3)
//y => x * mult(y)
//looks pretty good

mult(3)()
//3
//exactly what I want so far. 

mult(3)(4)()
//Uncaught TypeError: mult(...)(...) is not a function

sure enough,

mult(3)(4)
//NaN

Yet mult(3) looks good and typeof mult(3) === "function".

What gives? Can I not be this fancy in JS? Any why not?

3
  • 1
    Think about what happens when you call mult(3)(4). You are calling (y => 3 * mult(y)), where y is 4. What does mult(4) return? A function. What does 3 * <a function> return? NaN. Commented Feb 19, 2018 at 19:11
  • Probably take a look at the answers here: JS Curry function with Recursion Commented Feb 19, 2018 at 19:14
  • check this: stackoverflow.com/questions/35039020/… Commented Feb 19, 2018 at 19:17

3 Answers 3

2

In

mult(3)(4)

mult(3) yields y => 3 * mult(y).

Thus

(y => 3 * mult(y))(4)

becomes

3 * mult(4)

mult(4) yields y => 4 * mult(y).

3 * (y => 4 * mult(y))

is nonsense because you're trying to multiply 3 by a function. This is why you're getting NaN here, and NaN can't itself be further applied.


Possible solution:

function mkmult(acc) {
    return x =>
        x === undefined
            ? acc
            : mkmult(acc * x);
}

const mult = mkmult(1);

console.log(mult(3)());
console.log(mult(3)(4)());
console.log(mult(3)(4)(5)());

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

5 Comments

Wait actually, my comment was correct. Why are you doing const mult = mkmult(1) instead of just defining it as mult?
@PatrickRoberts Because otherwise mult() doesn't give you 1.
That wasn't a requirement though?
@PatrickRoberts It's in OP's code. And it makes sense anyway: We can generalize our function to something like a left fold: function fold_left(acc, op) { return x => x === undefined ? acc : fold_left(op(acc, x), op); } and in this case acc and x could even have different types. What you're suggesting is akin to foldl1, which 1) forces acc and x to have the same type and 2) doesn't work on empty lists.
@melpomene Thank you for showing me the error of my ways. You are my light in the darkness; the lighthouse of truth. Tonight, I will rest easy.
2

If you think about it, the mult should never be passed undefined since it represents the "current" or left side of the multiplication. The inner function receiving y is the one that should handle providing the result.

function mult(x){
    return y => {
        if (y === undefined) return x;
        else return mult(x * y);
    };
}

console.log(mult(3))
console.log(mult(3)())
console.log(mult(3)(4)())

2 Comments

It's beautiful. Utter beauty. I went with @melpomene 's answer because it clearly showed the error of my sinful ways in addition to how to provide the functionality I desire.
@doodlemeister, doesn’t work for empty set mult () due to improper assumptions about x
2

Here's another way that uses a secondary parameter with a default value

  • base case – x is undefined, return the accumulator acc
  • inductive case - x is not undefined – return a new lambda that asks for the next input y and recur using y and acc * x

const mult = (x, acc = 1) =>
  x === undefined
    ? acc
    : y => mult (y, acc * x)

console.log (mult ())             // 1
console.log (mult (2) ())         // 2
console.log (mult (2) (3) ())     // 6
console.log (mult (2) (3) (4) ()) // 24

Personally, I would use a known (explicit) empty value rather than relying upon the implicit undefined

const Empty =
  Symbol ()

const mult = (x = Empty, acc = 1) =>
  x === Empty
    ? acc
    : y => mult (y, acc * x)

console.log (mult ())             // 1
console.log (mult (2) ())         // 2
console.log (mult (2) (3) ())     // 6
console.log (mult (2) (3) (4) ()) // 24

Really though, mult as it is defined above is a toy. A more practical implementation would use variadic interface instead of the weirder currying with () used to signal a return value

// fixed arity
const mult = (x, y) =>
  x * y

// variadic interface
const multiply = (x = 1, ...xs) =>
  xs.reduce (mult, x)
  
console.log (multiply ())        // 1
console.log (multiply (2))       // 2
console.log (multiply (2, 3))    // 6
console.log (multiply (2, 3, 4)) // 24

2 Comments

I think I'm in love.
@Paul all of my answers on recursion are answered from a functional programming perspective. I hope you find some of them helpful ^_^

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.