Challenge - Function that only runs with no arguments (intermediate / advanced functional programming)
Tags: programming, functional, learning, javascript
Published: 2021-05-04 09:59:27

{% youtube Sz-m3i66DZs %}

This is an intermediate / advanced functional programming challenge I had to solve in an interview. I found it very interesting and it had me thinking for a while.

If you're interested in a brain teaser, have a go. You can also complete it using classes and methods if you prefer, but we'll use functions in this article.

(If you don't know a lot about functional programming and want to look at that first, then check out [functional programming - the ultimate beginner's guide](https://programmingduck.com/articles/functional-programming-introduction).)


## The challenge

There were two parts to the challenge. Part 1 and part 2. Each part increases in difficulty.

I've also added a small part 3.


### Challenge, part 1

Design a function that you can keep calling in a chain as long as you keep passing in arguments. For example, `foo(argument1)(argument2)`. During this time, it shouldn't do anything except collect the arguments. Finally, when you call the function without an argument, it should call a real function, such as `console.log`, with the arguments you've passed in so far.

Here is a code example showing how this function should behave:

```javascript
foo('a'); // nothing happens
foo('a')('b'); // nothing happens

foo(); // logs an empty line (calls console.log())
foo('a')(); // logs 'a' (calls console.log('a'))
foo('a')('b')() // logs 'a b' (calls console.log('a', 'b'))
foo('a')('b')('c')() // logs 'a b c' (calls console.log('a', 'b', 'c'))'

// optionally, the function can accept more than one argument at once
foo('a', 'b')() // same as foo('a')('b')()
foo('a')('b', 'c')() // same as foo('a')('b')('c')()
```


### Challenge, part 2

Implement a function similar to question 1, but make it "stateless"?

Different function calls shouldn't share state with each other.

For example:

```javascript
const a = foo('a');
const ab = a('b');
const abc = ab('c');

a(); // logs 'a' (calls console.log('a'))
abc(); // logs 'a b c' (calls console.log('a', 'b', 'c'))
ab(); // logs 'a b' (calls console.log('a', 'b'))
```

The difference compared to part 1, is that calling `a('b')` shouldn't also add the argument `'b'` to the call for `a()`. `a()` should log `'a'`, not `'a b'`.


### Challenge, part 3

Make a function similar to the one for part 2, but the first time it's called it must be called with a single argument. That argument should be the function it will run in the end.

Then, as normal, the function should keep accepting and collecting arguments. When it's finally called without an argument, it should execute the function you passed in as the first argument with the arguments it has collected.

For example:

```javascript
const log = actWhenCalledWithoutArguments(console.log);
const a = log('a');
const ab = a('b');
const abc = ab('c');

log(); // logs an empty line (calls console.log())
a(); // logs 'a' (calls console.log('a'))
abc(); // logs 'a b c' (calls console.log('a', 'b', 'c'))
ab(); // logs 'a b' (calls console.log('a', 'b'))
```


## Hints - Part 1

So what would your solution to these questions be? Before reading on, try solving them on your own.

If you need help, read on for some hints.


### Hint 1 - Currying

For the first hint, notice that these functions sound a lot like currying. Curried functions also accept arguments multiple times, before finally executing the "real functionality" with all of the arguments they've collected. This is very similar to what you're trying to do in this challenge.

In particular, the `curry` utility seems very relevant. That's a function that takes a normal function, which isn't curried, and turns it into a curried function. In its implementation, it has a mechanism for storing arguments and then calling a function with all of the stored arguments later.

So, you may want to start by implementing the `curry` utility first. Looking at its source code may be helpful for the challenges.

Feel free to try to implement it yourself.

Otherwise, here is an example implementation:

```javascript
function curry(fn, arity = fn.length) {
  function execute(...args) {
    if (args.length === arity) {
      return fn(...args);
    }
    function gatherMoreArgs(...moreArgs) {
      return execute(...args, ...moreArgs);
    }
    return gatherMoreArgs;
  }
  return execute;
}

// example usage
function foo(a, b, c, d) {
  console.log(a, b, c, d);
}

const curriedFoo = curry(foo, 3);
curriedFoo('a')('b', 'c')('d'); // logs 'a b c d' (console.logs('a', 'b', 'c', 'd'))
```


### Hint 2 - More functions

For the challenges, you don't need to have just one top-level function. Just like the `curry` utility, you can have more functions. The `curry` utility itself is made up of 3 nested functions in total.

If you're stuck on your solution, explore what you can do with 1 function, 2 functions or even 3 functions, nested or not.


### Hint 3 - How to gather arguments for part 1

This hint is more spoiler-heavy. It talks about a possible implementation for part 1.

One way to gather the arguments is to have an array. Then, every time the function is called with arguments, push them to that array.


## Solution - Part 1

Alright, if you're ready, here's the solution for part 1:

```javascript
const logWhenCalledWithoutArguments = (...args) => {
  const argsSoFar = [];

  const execute = (...moreArgs) => {
    if (moreArgs.length === 0) {
      console.log(...argsSoFar);
    } else {
      argsSoFar.push(...moreArgs);
      return execute;
    }
  }

  return execute(...args);
}
```

`logWhenCalledWithoutArguments` is a higher order function (a function that returns another function). This is needed so that it can create a private array (`argsSoFar`), that exists only for that invocation of `logWhenCalledWithoutArguments`. That way, different calls of `logWhenCalledWithoutArguments` won't share the same arguments.

Other than that, the `execute` function does the real work. First, it checks whether it was called without arguments. If that's the case, then it calls `console.log` with all of the arguments collected so far. Otherwise, it adds the new arguments to `argsSoFar`. Then, it returns itself so that the user can call the function again and keep providing arguments.

At the end of `logWhenCalledWithoutArguments`, we immediately call the `execute` function and return its result. That way, we start checking and collecting arguments from the first call.


## Hint - Part 2 - Making it stateless

This is another spoiler hint.

Part 2 of the solution can't share state like part 1 did. With the solution for part 1, this code wouldn't work:

```javascript
const a = logWhenCalledWithNoArguments('a');
const ab = a('b');
a(); // incorrectly logs 'a b'
ab(); // correctly logs 'a b'
```

So you can't use a shared array like in part 1.

Instead, you have to find a way to pass in the arguments across different calls using closures.


## Solution - Part 2

Alright, if you're ready, here's the solution to part 2.

```javascript
const logWhenCalledWithoutArguments = (...firstArgs) => {
  const gatherArgs = (...args) => {
    const execute = (...moreArgs) => {
      if (moreArgs.length === 0) {
        return console.log(...args);
      }
      return gatherArgs(...args, ...moreArgs);
    }
    return execute;
  }
  const firstExecute = gatherArgs();
  return firstExecute(...firstArgs);
}
```

This function has a lot of similarities to the solution for part 1. The main difference is that, instead of capturing arguments in an array, we use the function `gatherArgs` to hold the arguments.

Also, just like before, we run the `execute` function straight away, to check if we got an argument on the first call.


## Solution - Part 3

For part 3, the first call must be a single argument, the function to execute at the end. Everything else is the same.

If you're ready, here's the solution:

```javascript
const actWhenCalledWithoutArguments = (fn) => {
  const gatherArgs = (...args) => {
    const execute = (...moreArgs) => {
      if (moreArgs.length === 0) {
        return fn(...args);
      }
      return gatherArgs(...args, ...moreArgs);
    }
    return execute;
  }
  return gatherArgs();
}
```

The solution is very similar to the solution for part 2.

The main difference is that we don't need to check for an argument on the first call. As a result, we don't need to run `execute` straight away. All we have to do is return it.

The other difference is that we replace the call to `console.log` with `fn` (the first argument).

Other than that, everything else is the same.


## Final notes

Anyway, that's it for this challenge.

I hope you enjoyed it.

Let me know if you completed it, and whether you thought it was easy or difficult, in the comments. It's not the kind of thing you have to implement at work every day.

Also, if you have any similar challenges, or if you have any tips or feedback for this one, then please leave a comment.

Alright, see you next time.