Functional programming in javascript - appendix (a) (b) (c)

I am trying to wrap my head around functional programming in js.

I understand that adding (3) (5) would be:

function add(x) {
    return function(y) {
        return x + y;
    };
}

      

How can I change this function, so add (3) (5) (7) (8) returns 23, or adds (1) (2) (3) returns 6?

+3


source to share


5 answers


Without changing your definition for, add

you will need to bind calls to add

as (add(add(add(3)(5))(7)))(8)

.

to clarify, this expression is broken down into:

add(3) //returns a function that adds 3
add(3)(5) //returns 8

add(add(3)(5)) // returns a function that adds 8
(add(add(3)(5)))(7) // returns 15

add ((add(add(3)(5)))(7)) //returns function that adds 15
(add(add(add(3)(5))(7)))(8) //returns 23

      

Breaking it down even further, as @zerkms mentioned (and assigning our function definitions to variables), we see how chaining works add

:



var add3 = add(3) //returns a function that adds 3
add3(5) //returns 8

var add8 = add(add3(5)) // returns a function that adds 8
add8(7) // returns 15

var add15 = add(add8(7)) //returns function that adds 15
add15(8) //returns 23

      

We chain the result of the previous call to add

.

We assume that if a add(3)

function returns that adds 3, and then you add 5 to that number, you can pass that value 8 to another call add

to make another function that adds 8 to that argument.

+3


source


you can do something like this.

function add(x) {
  return function(y) {
    if (y) {
      return add(x+y);
    }
    return x;
  };
}

      

Here you can call as many times as you like.



add(4)();
add(4)(5)(9)();
add(1)(2)(3)....(n)();

      

Link example

+2


source


What about:

function add(x) {    
    return function(y) {
        return y == 0 ? 
               x + y :
               add(x + y);
     };
}

add(3)(5)(7)(8)(0) // ==> 23
add(1)(2)(3)(0)    // ==> 6

      

The trick here is that it knows when to return a function or response by argument value. The "stop" value might be something really, but in my example it 0

is that triggers the response.

+1


source


TL; DR

You cannot call a number as a function. So use a compound template and return the accumulated function at each iteration. Here is one way to implement add

from a factory using tests and examples: http://jsbin.com/qaqiqu/edit?js,console

More details:

var factory = (function (){
  "use strict";

  var reduceFactory = function (options) {
    var defaultReduce = function (x, y) { return x + y; },//add
        noLog = function () {};

        function store(x) {
          if (options && options.log) {
            options.log(x, options.acc, options);
          }
          options.acc = options.f(options.acc, x);
          store.result = options.acc;
          return store;
        }
        //default options
        options = typeof options !== 'undefined' ? options : {};
        options.f = typeof options.f !== 'undefined' ? options.f : defaultReduce;
        options.acc = typeof options.acc !== 'undefined' ? options.acc : 0;
        options.log = typeof options.log !== 'undefined' ? options.log : noLog;

        return store;
      };

  return reduceFactory;
}());

//example usage
(function (f) {
  var add = f(),
      add1 = f(),
      add2 = f(),
      add3 = f(),
      add4 = f(),
      clear = function(f) {
        return f(-f.result);
      };
  //how to use a single function
  console.log('add(3)(5)(7)(8).result = ' + add(3)(5)(7)(8).result);
  clear(add);
  console.log('add(1)(2)(3).result = ' + add(1)(2)(3).result);

  //how to use factory functions
  console.log('add1(3)(5)(7)(8).result = ' + add1(3)(5)(7)(8).result);
  console.log('add2(1)(2)(3).result = ' + add2(1)(2)(3).result);

  //factory functions can continue to grow as needed  
  add3(3)(5);
  add3(7)(8);
  console.log('add3(3)(5); add3(7)(8); add3.result = ' + add3.result);

  add4(3);
  add4(5);
  add4(7);
  add4(8);
  console.log('add4(3); add4(5); add4(7); add4(8); add4.result = ' + add4.result);

}(factory));

      

When the add function finally returns a number, then the number cannot be called as a function. The usual way around this is to specify how many arguments are required before the number is finally returned, as suggested in other answers. Some answers have suggested the functional terminator ( 0

, undefined

) as a new approach, but from the OP's comments -

"looking for a solution without the need for completion () or (0)"

Using a composite template, we can return a function that can also be used as a number by providing it with a result property. This way we can keep accumulating the result as the number of function calls increases. We can use the factory function to create instances add

if we need to make individual additions. Alternatively, we can reset to point one add instance to zero when a separate add is required.

I also wrote my own set of test assertions with the following test cases:

var testCases = [
        {arguments: [0], expectedResult: 0},
        {arguments: [5, 0, 0, 5, 10, -1], expectedResult: 19},
        {arguments: [1], expectedResult: 1},
        {arguments: [1, 2], expectedResult: 3},
        {arguments: [1, 2, 3], expectedResult: 6},
        {arguments: [3, 5, 7, 8], expectedResult: 23},
        {arguments: [3, 4], expectedResult: 7},//{acc: 1000}
        {arguments: [1, 2], expectedResult: 1003, factoryOptions: {acc: 1000}},
        //example tests with logging to debug
        //{arguments: [1, 2], expectedResult: 3, factoryOptions: {f: add, log: addLog}},
        //{arguments: [3, 4], expectedResult: -7, factoryOptions: {f: sub, log: subLog}},
        {arguments: [3, 4], expectedResult: -7, factoryOptions: {f: sub}}
      ]

      

I used inversion of control and made the reduce function an optional parameter to the factory. For example, instead add

you can use sub

(i.e. X - y) as shown in the test cases.

0


source


Simply put, the title and body of the question refer to different things.

First, functional programming is not related to n-ary functions. You can build the examples shown earlier, for example with recursion (to keep things immutable); however, you are sacrificing the aggregate of the function. For example, 0-terminated functions can get corrupted at runtime if you have external parameters that you have not confirmed to be non-null.

A solution that will enable totality, either accepts non-zero integers (JavaScript only has the type "number"), but also gets zero to get the value.

Alternatively, the monad can allow you to combine properties of a number, returning a function that can be bound further. (But then you won't have a pure "number", but rather a regular "appended" monad. Useful, but should be implemented.)

Otherwise, the add function might just be the sum of the list. Then it can have from 1 to N elements to calculate, it just won't be arguments.

Now why the arity variable isn't very functional, the reasons are pretty fundamental. First, if you have a function f (a) (b) (c) (d) ... (n) what is the type? Without types, you lose many of the basic aspects of this area. If it always returns the type of itself (which is possibly shown by the recursive example), then it is useless, as it cannot actually generate side effects. Now, to make it useful while keeping it clean, we can't just restrict the scope (ie) to Zero, because then we are "lying" since zero is part of the valid input; the result simply won't be of the same type as the function. Hence, if we want to create a wrapper function that validates all inputs as non-0 (and ignores 0), and after the last term called 0, we do the same as discussed earlier.just in a different way.

For example, we could have "Maybe number" as input, and when it's not a number, the result is returned (thus aggregate, but functionality). Or we could have a list, and after the list is complete, we "discover" the change again (elsewhere, that is, seeing if the vector has been exhausted or there are no more members in the list.)

It's implemented anyway, it does the same. The difference is in usability, error rate, readability, maintainability, accepted data types, language or perspective.

A side note on why this is all so complicated: Providing an argument is itself the same as calling a function. So f (a) (b) (c) (d) has 4 applications, none of them being "last", they are ordered, but there is no implicit finite boundary. The creation of which again goes back to my previous statement about how this is the exact problem with different implementations.

All solutions follow this pattern in one way or another. Chaining a binary append operation results in a very similar data flow to recursion; however the border is hardcoded. Recursion is similar to OOP style, where the sum is cumulative and can be obtained at any point. Which is like "Add" Monad, which will contain both the add function along with the current state, and essentially the "next" user after the add function drops the function and retains the state.

I think the simplest solution for this problem is: [1,2,3,4,5] .reduce (function add (sum, x) {sum + x}, 0)

0


source







All Articles