Not every function needs a name

macsikora

Pragmatic Maciej

Posted on July 1, 2019

Not every function needs a name

Lets talk about anonymous functions. Use it or not? Is it a shame to use not named functions, are the only reasons for that - laziness, and un-creativity? I claim that not, I claim there is a sense to keep using this language construct.

Motivation

The article was written as a response for Kyle Simpson tweet, and also opinions presented in his book "Functional Light Programming" where he claims that anonymous functions should not be used at all. I personally see these opinions as radical and not fair.

Example, give me an example

Lets say there is a need for data transformation. We have a list of movies, and this list need to be filtered and mapped.

// anonymous functions
movies
  .filter(movie => movie.allowedAge <= age)
  .map(movie => movie.name)
Enter fullscreen mode Exit fullscreen mode

As you can see I've declared two inline, anonymous arrow functions to achieve the goal. Now I will show how to achieve the same with named functions

// named functions
const ageIsAllowed = movie => movie.allowedAge <= age
const propName = movie => movie.name

movies
  .filter(ageIsAllowed)
  .map(propName)
Enter fullscreen mode Exit fullscreen mode

This is simple, and that was my goal, but I claim that there is no win in naming these functions. I can say more, I had a real problem with naming the first one, and I feel that without looking into the implementation, still you have not enough information to be sure what this is doing.

However I think that at this stage, both solution are almost equal, if I would see latter in the code, I would say - it is ok. But it is very popular to go further, and take out such functions from the lexical scope, then this starts to be less the same. Let me show you

const ageIsAllowed = age => movie => movie.allowedAge <= age
const propName = movie => movie.name

/* other code - not related */

const allowedByAgeMovieNames = (movies, age) => {
  movies
  .filter(ageIsAllowed(age)) // additional complexity by partial application
  .map(propName)
}
Enter fullscreen mode Exit fullscreen mode

The problem is a lack of cohesion. To track what you are doing in allowedByAgeMovieNames you need to jump through the file, and the worst would be to put these functions outside the file. Then you need to jump there also. During the process, functions lost access to the lexical scope. As now I cannot use closure, because functions were declared outside, I need to use partial application to provide the age argument. It is not bad, though it is an additional thing to be done here.

Simple functions, what else?

I see few major places where anonymous function is a best tool for the job:

  • simple function with self explanatory implementation
  • function being a composition without any imperative code
  • function being a proxy for other function
  • parent function is descriptive enough

Simple function (already explained)

map(x => x + 1)
Enter fullscreen mode Exit fullscreen mode

Composition

Anonymous function as a composition of others named functions.

map(element => getFirstLetter(getName(element)))
Enter fullscreen mode Exit fullscreen mode

The body has no imperative logic, it is self-explanatory. No need for naming, and naming would give as nothing really better, something like getFirstLetterFromName is not more readable then above.

Proxy

It is common situation that in the codebase exists a function which match the need, but the interface do not match the wanted interface. For example, there is a demand for event handler, but our function has different arguments.

onClick(ev => movePlayer(ev.clientX, ev.clientY))
Enter fullscreen mode Exit fullscreen mode

Also it is good to have only minimum amount of arguments. There is a small chance that any of your functions will need the whole Event object.

Parent function is descriptive enough

const getActiveUsers = users => users.filter(user => user.isActive)
Enter fullscreen mode Exit fullscreen mode

As you can see, parent function explains correctly the whole function. There is no need to name also the predicate used in filter. In this case, even more complicated implementation of the predicate would be acceptable in form of an anonymous function.

Should I always use anonymous functions? No!

My call here is, if function body contains imperative and not trivial implementation to deal with, you just should name it. The same rule I apply for conditions, if my condition is not trivial I give it a name. So instead of

if (x < y && z > w || v === true)
Enter fullscreen mode Exit fullscreen mode

I prefer

const someExplanation = x < y && z > w || v === true

if (someExplanation)
Enter fullscreen mode Exit fullscreen mode

But I don't think we should name simple conditions or name trivial functions. As naming can be harder then understanding the function itself.

What about point free style?

Nothing, because any of above examples are in PFS. As JS has no tools to achieve PFS. In order to do so, you need or create the tools, or use external library like Ramda. The crucial thing in PFS, is compose operator/function and currying. I will try to touch this subject in some future article. But want to be clear, if somebody is trying to find in original arguments, something about point free, then I say that it has nothing to it.

Conclusions are not black or white, but gray

In any case I don't want to persuade anybody to stop naming functions. My statement is simple: don't get into such fake limitations. Sometimes there is no real sense in naming simple things, and the worst you can do is to name simple thing by the wrong name. Anonymous function is a tool, a nice tool to use when there is no need for code sharing, if the implementation is just bound to the context and live in it. To name it, or to not. This choice I leave to you.

💖 💪 🙅 🚩
macsikora
Pragmatic Maciej

Posted on July 1, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related