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)
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)
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)
}
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)
Composition
Anonymous function as a composition of others named functions.
map(element => getFirstLetter(getName(element)))
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))
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)
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)
I prefer
const someExplanation = x < y && z > w || v === true
if (someExplanation)
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.
Top comments (4)
I agree that arrow callback functions don't need names, if it is a simple function of just a few lines. If there is more complex logic I prefer to extract that into a named function and pass it as the callback, e.g.
But what drives me crazy is top-level anonymous arrow functions. I mean this:
I think functions like this should always have a name.
I understand Kyle's dislike of anonymous functions but I think he takes it a bit far.
Just my opinion.
Generally what you said is almost exactly what I tried to describe in the article. With this difference that I don't understand Kyle dislike for anonymous functions at all, his arguments like - if you have no clue how to name something, name it - todo, are for me just bizarre. And I am not joking, he is really proposing that in his book.
I vote here for commons sense, name things when it has sense to name them, and if you do, name them accurate. There is nothing worst then bad names.
Thanks for the comment!
I think one of the main criticisms is that they just show up as
(anonymous)
in a stack trace, so it might make debugging harder.Honestly, that's the only issue I can think of.
Maybe I would not fully agree with optional nature of the function name. But IMOP there is just common sense needed, as always. Such edge opinions like - never use it and we get such funny structures