Please do not do the following anti-patterns and call it Functional Programming or Functional style or Modern JS Syntax!
Anonymous functions galore
Yeah, anonymous functions look mathy-nerdy, but they are just functions without a name. So the following things do not have any value:
Exporting anonymous functions 馃ズ
Only a true incarnation of evil would write a code that cannot be ever safely searched in a codebase:
// in evil-function.js file
// this looks so mathy, I am mentally superior to my peers
export default (name) => console.log(`Try importing this with your IDE, ${name}`);
// in any other file
import evilFunction from './evil-function';
// in another file (note `el typo`)
import evilFunctino from './utils/evil-function';
So an IDE would figure out your intent through having a sort of map of all available items: reserved words, objects defined in available scopes at the cursor, named exports and default exports with names.
Named exports.
Does it a ring a bell? I also made one more real-life twist to it: the guy who had been unlucky enough to having to work with that horrible function has made a typo. Now when colleague B comes over to investigate a bug related to evil-function
, they would search for import evilFunction
to find all usages. And usages they will find except the one(s) with the typo 馃ズ. Or when somebody used a synonym like satanicFunction
.
Usually the people who also claim that they don't use TypeScript
because they write code faster than to know what is the exact input and exact output of their functions don't think there is any problem with this.
Sigh.
Composing anonymous functions
How the hell should I know what this train-wreck does if I don't look up every goddamn return value of every goddamn function involved:
const myComputedValue = (stuff) => (_, factoryFn) =>
addLayer(stuff)({
x: 134,
y: 134}
)({ moreStuff: stuff.otherStuff })
(factoryFn('LayerObject'), true);
What on Earth this shit does? This is not Functional Programming, this is code-obfuscation.
Why do I need to skip a parameter with _
? What if I forgot the parameter order and pass (factoryFn, _)
? Why is not the design of the function's interface force me to specify my parameters?
Look:
function objectCreator({ options, factoryFn } = {
options: { cloneValues: true }
}) {
// ...
}
// usage:
objectCreator({ factoryFn }); // can't miss it
// the equivalent of objectCreator(factoryFn, _) would be this nonsense:
// you would never write it...
objectCreator({ options: factoryFn, factoryFn: undefined });
Note that we have improved the interface of the function: the order of parameters now can be freely interchanged, values not being used can also be omitted.
In short: the parameter list is now commutative.
Moving on to the nameless functions returned, why don't you return something more meaningful that can be chained?
const prepareForUpload = (x, y) => {
const transformedValue = { name: x, value: y };
return (x) => api.upload(x);
};
// What does the second thing do?
// You have to check the function definition.
prepareForUpload('Robert', '2$')();
// or do this:
function prepareForUploadWithObject(x, y) {
const transformedValue = { name: x, value: y };
return {
upload: () => api.upload(transformedValue)
}
}
// clear immediately
prepareForUploadWithObject('Robert', '200$').upload();
Is version two still pass as Functional Programming? YES! But much clearer on what it really does!
So please don't do this, functions returning functions that can be called without ever specifying what the hell they do is the ultimate cringe
.
If you are too busy to type some extra characters you will be very busy double checking everything.
Naming anonymous function
Technically there is nothing wrong with using them if you are aware what anonymous functions are and what not. But why do you name something that was meant not to have a name?
const anonymousWorkerFunction = (stuff) => {
//... 20 lines of code
};
// vs
function doSomeWork(stuff) {
//... 20 lines of code
}
const
variables only exist in the block they are defined and also they are not even hoisted - it means if you want to use anonymousWorkerFunction
before you defined it, you will get a juicy Uncaught ReferenceError: Cannot access 'anonymousWorkerFunction' before initialization
.
There are special cases where using them makes total sense: see this excellent opinion piece by Kyle Simpson, also known as getify
.