I want to do some Grumpy Old Man Complaining, but first some Grumpy Old Man Nostalgia: for Perl, the hacker's friend glue language of the '90s.
The world has moved on from Perl, but I will always be grateful to it as my first introduction to 5 hugely powerful concepts - maps, automatic memory management, first class strings, regex, and loose (duck) typing - all things big in modern js programming and all things that university Programming 101 C didn't have.
(And later, I could put Perl on cheap rented webspace, and was able to personally do just about anything that could be done on server and browser of the time - empowering!)
But Perl hard a dark side - often gnarly syntax that could let you make "write-only code", inscrutable to any poor sap who had to try to update it (maybe your own future self). The apotheosis of this was Perl Golf where people would use the intricacies of the punctuation to make the shortest possible program to solve a task.
I think that thinking is some of what killed Perl. And it didn't have to be that way! You could write in stodgy old verbose styles and keep up maintainability - I have some bits on my server that I literally wrote 20 years ago, still chugging merrily away, and when I have to update it... well, it's not great since I'm so rusty at it, but it's generally easy enough to absorb the intent of my past self. (Vs Perl Gold that was inscrutable as soon as it was written.)
So, fast forward to the mid '10s. I sometimes fear Javacript/ECMAScript is going down the Perl Golf road. Or at least, you have too few symbols playing too many roles, and so all this code becomes very context dependent.
The hip coders frickin' hate wordy anonymous function(){ } everywhere, so the premier improvement is being able to write
var addone = function(arg) { return arg + 1; }
as
let addone = (arg) => { return arg + 1; }And I kind of like that.... => is a nice visual pun that emphasizes the transformation that is so critical to functional, side-effect-free programming.
But that wasn't enough for the hip coders, because they want this kind of anonymous value transformation function to be some common and instinctive that if there's just one argument, you can drop the parens to the argument.
let addone = arg => { return arg + 1; }
This is where they start to lose me. I guess when I read it I tend to see the "addone = arg" as an assignment chunk first, but the important grouping is the "arg => { return arg + 1; }".
And then to make it better, or worse - we dropped "function", if we are just doing one thing we can drop the "return"!
let addone = arg => a + 1;
So on a toy oneliner, that's easy enough to read, but since the whole point is who smoothly this stuff can be embedded in more complex operations... you really have to be on your toes. Everything is much more context dependent than it used to be. (Which is kind of ironic for a system that embraces modularity and being side effect free.) I hope as I get more fluent in it, this will be less of a problem and I'll get the benefit of thinking of the lil function as a singular unit.
So what did the hip coders do with all those curly braces they saved up? Object destructuring!
let o = {a: 42, b: true, c: 'haha'};
let {a, c} = o;
//now you have a and c to work with as local variables
//and showed your disinterest in b
I kind of like this, and love how you can turn the JS trope of "this function's argument is a single map of lotsa keys" into something that lets you use plain old local variables, but- ambiguity is growing. A lonely {foo} might be either an argument destructuring, or a return value for a fat arrow function. You can't know without investigating around it.
So we have a "{" that might be
- starting some good ol' JSON
- a function or conditional block,
- or a bit of object destructuring,
- or (in the JSX that React loves) wrapping a simple expression that will explode if you put in the semi-colon,
and a "(" that might be
- wrapping arguments for a function,
- or a conditional for an if
- or a grouping in general
- Simple key value - let myMap = {key:value}
- Destructure with rename: let {newVarName:oldKey} = oldMap
I did a bad regular experession and tried to find the most heinous examples I could in the React Training materials, I came up with
<Route
render={({ match }) => ( //...
the first { is JSX saying that's an expression, the ( starts a fat-arrow argument, the next { is doing some destructuring... and the final ( is starting an expression for the invisible return statement.
Oh of course the ... is just my placeholder in a comment, it isn't the spread operator... (Jackson sometimes used ... and I wasn't always positive which he meant.)
To be clear, this is complaining isn't me trying to kick against which way the wind is blowing - while I have reservations about modern UI toolkits being such a moving target, with a LOT of flavor of the month and "welp, here's the new version, hope you didn't set up npm or yarn to automatically pull the latest" (I have 18 year old Perl code going merrily along, but my 18 month old React experiment had to be rewritten) and I'm a little nervous about building a long term structure on it. (Just the image of interviewing candidates in 2021- "yeah, sorry, our React is in that old 2018 style, we know, we should really refactor it but its too much to do at once, even with the unit tests.")
React is still pretty damn cool, and I think it's answer to the age old "how do we relate DOM bits with script-y bits" solution is good and relatively transparent, and error messages give you some idea of what you did wrong (which is more than I can say for my time with Angular)
My coworker Peter says, half-joking, we should consider using more punctuation to avoid this overloading, now that Unicode is a thing - like «french-style quotetation marks». (Kind of like APL used to do with "←" for assignment) I think this idea is a non-starter, cranky engineers like me barely remember to put in proper quotes in UIs, since inch marks and foot marks are good enough and can be directly typed on the keyboard.
UPDATE: my manager Eric came up with a good example. He slacked me
.map(({ id }) => id)code i just wrote, but then changed to .map(listing =>listing.id)because it’s so much easier to read the second wayHe's absolutely right! And I'm not a total stick in the mud, I think the latter way is clearly better for thinking about and reading than
.map(function(listing) { return listing.id; })