Not so easy functional programming in JavaScript
Introduction
JavaScript allows for operating on arrays in a functional way, e.g. using filter
or map
functions. As an argument for these functions we can pass lambda expression or function reference. Is there a difference between them? The answer is yes.
What’s the problem?
In our project we are builing a mapping using String.fromCharCode
function. To simplify the usage of this function looked similar to:
[66, 67, 68].map(v => String.fromCharCode(v))
When we run this code with node we received [ 'B', 'C', 'D' ]
, but when we decided to refactor it to use function reference the result was different:
> [66, 67, 68].map(String.fromCharCode) [ 'B\u0000\u0000', 'C\u0001\u0000', 'D\u0002\u0000' ]
What happened?
To find the reason of this behavior, let’s first play with function String.fromCharCode
alone:
> String.fromCharCode(66) 'B' > String.fromCharCode([66, 67, 68]) '\u0000' > String.fromCharCode(66, 67, 68) 'BCD'
String.fromCharCode
deals with various types and numbers of arguments.
Now, let’s examine the function map
:
> [66, 67, 68].map(v => v) [ 66, 67, 68 ] > [66, 67, 68].map((v, u) => [v, u]) [ [ 66, 0 ], [ 67, 1 ], [ 68, 2 ] ] > [66, 67, 68].map((v, u, w) => [v, u, w]) [ [ 66, 0, [ 66, 67, 68 ] ], [ 67, 1, [ 66, 67, 68 ] ], [ 68, 2, [ 66, 67, 68 ] ] ]
map
, like many other array functions, passes always three arguments to function. First is current value, second is the index of current value and third is the whole array.
It means that passing String.fromCharCode
to map
function under the hood looks like this:
> [66, 67, 68].map((v, u, w) => String.fromCharCode(v, u, w)) [ 'B\u0000\u0000', 'C\u0001\u0000', 'D\u0002\u0000' ]
and it is equal to the initial example.
Conclusion
We have to be careful when we want to use a function which can take more than one argument, but we want to pass only the value. We have to pass the function as a lambda expression:
> [66, 67, 68].map(v => String.fromCharCode(v)) [ 'B', 'C', 'D' ]
or create another function which ensures that only the first argument will be passed to desired function:
> const useOnlyValue = f => v => f(v); undefined > [66, 67, 68].map(useOnlyValue(String.fromCharCode)) [ 'B', 'C', 'D' ]
Published on Web Code Geeks with permission by Dominik Przybysz, partner at our WCG program. See the original article here: Not so easy functional programming in JavaScript Opinions expressed by Web Code Geeks contributors are their own. |