Search code examples
javascriptlistobjectfunctional-programmingchain

How to store data of a functional chain?


A simple function below:

const L = a => L;

forms

L
L(1)
L(1)(2)
...

This seems to form a list but the actual data is not stored at all, so if it's required to store the data such as [1,2], what is the smartest practice to have the task done?

const L = (a) => {
 // do somthing
  return L;
};

I would prefer this concise arrow function style, and do not want to destroy the outer structure as much as possible. Surely, I understand some outer structure modification is required, but I am curious what is possible especially in functional style not OO.

The specification is simply to store data of the function chain.

Any ideas? Thanks.

An initial simplest approach would be:

const L = (a) => {
  L.val = a;
  return L;
};
L.val = L;

can do some, but no data accumulation.

{ [Function: L] val: [Circular] }
{ [Function: L] val: 1 }
{ [Function: L] val: 2 }

Notice:

Every list should be independent for the accumulation.

L(3)(4)

will return [3,4] not [2,3,3,4] with prior accumulation of another lists.

Advanced topic!

How to store data of a functional chain of Monoidal List?


Solution

  • Function currying and variadic arguments don't really work together. It's a restriction made obvious once you realize that the following two expressions are incompatible

    L (1)     -> [ 1 ]
    L (1) (2) -> [ 1, 2 ]
    

    Above L (1) returns a list, but in the second expression we expect L (1) to be a function that we can apply to 2. L (1) can be a list or it can be a function that produces a list; it cannot be both at the same time.

    This is why others have proposed things like .list to get the actual value out. You can do that but know that using object properties or relying upon mutation is not necessary. You can use any signal of your choosing

    const L = (x, acc = []) =>
      x === undefined
        ? acc
        : y => L (y, [...acc, x])
        
    console.log
      ( L ()              // []
      , L (1) ()          // [ 1 ]
      , L (1) (2) ()      // [ 1, 2 ]
      , L (1) (2) (3) ()  // [ 1, 2, 3 ]
      )

    We can abstract away the optional argument by using an auxiliary helper function. This technique is similar to the solution you found but here we avoid awkward assignment of values to function properties and instead use simple variables and non-mutating actions

    const L = init =>
    { const loop = (acc, x) =>
        x === undefined
          ? acc
          : y => loop ([...acc, x], y)
      return loop ([], init)
    }
    
    console.log
      ( L ()              // []
      , L (1) ()          // [ 1 ]
      , L (1) (2) ()      // [ 1, 2 ]
      , L (1) (2) (3) ()  // [ 1, 2, 3 ]
      )

    Or seeing as though your requirements are somewhat flexible, get creative with a more flexible encoding

    const List = x =>
      k => k (x)
      
    const append = x => xs =>
      List ([ ...xs, x ])
    
    const prepend = x => xs =>
      List ([ x, ...xs ])
      
    List ([]) (append (1)) (console.log)
    // [ 1 ]
    
    List ([ 2, 3 ]) (append (4)) (append (5)) (prepend (1)) (console.log)
    // [ 1, 2, 3, 4, 5 ]

    It's fun to push JavaScript's permissive syntaxes to their limits, but variadic functions are best defined using spread arguments

    const L = (...values) =>
      values
    
    console.log
      ( L ()         // []
      , L (1)        // [ 1 ]
      , L (1, 2)     // [ 1, 2 ]
      , L (1, 2, 3)  // [ 1, 2, 3 ]
      )

    A less contrived example demonstrates a better use case

    const max = (x, ...ys) =>
      ys.length === 0
        ? x
        : max2 (x, max (...ys))
        
    const max2 = (x, y) =>
       x > y ? x : y
       
    console.log
      ( max (1, 5, 3)     // 5
      , max (5, 2, 9, 7)  // 9
      , max (4)           // 4
      , max ()            // undefined
      )