During the Software Passion Summit in Gothenburg, I had the privilege to talk to Douglas Crockford, the grandfather of Javascript and the creator of JSON and JSLint
While I think that JSLint is an awesome tool, I also think that it is too opinionated and therefore I use JSHint, an alternative that allows me to tweak more of the parameters to fit my style of coding. Douglas think that this is sacrilege, but I disagree. There are two major things I disagree with in JSLint and they are:
- I must have braces for every conditional and loop statement. I don't like this, I want my code to look as clean as possible and lots of braces clutters it.
- I must declare my vars at the top of the function. I don't like this because it complicates refactoring.
So this is what I think:
All braces that are not necessary should be omitted.
and
Variables should be declared on first usage.
In Javascript, and other C-like programs, there are only two valid cases for using braces. It is when I am declaring objects and functions (and in other C derivates: classes).
So what does this mean in practice?
Lets pretend that I have a function that looks like this:
function feedAnimals(animals) { var i, animal, grass, water, meat; for (i = 0; i < animals.length; i++) { animal = animals[i]; if (animal.isHerbivore()) { grass = prepareGrass(animal); water = prepareWater(animal); animal.feed([grass, water]); } else if (animal.isCarnivore()) { meat = prepareMeat(animal); water = prepareWater(animal); animal.feed([meat, water]); } } }
The above fulfills JSLint style, but it does not fulfill my braceless since it has a bunch of unnecessary braces. This is how the code would look like in braceless style:
function feedAnimals(animals) { for (var i = 0; i < animals.length; i++) feedAnimal(animals[i]); } function feedAnimal(animal) { if (animal.isHerbivore()) feedHerbivore(animal); else if (animal.isCarnivore()) feedCarnivore(animal); } function feedHerbivore(herbivore) { var grass = prepareGrass(herbivore); var water = prepareWater(herbivore); herbivore.feed([grass, water]); } function feedCarnivore(carnivore) { var meat = prepareMeat(carnivore); var water = prepareWater(carnivore); carnivore.feed([meat, water]); }
Braceless style encourages breaking the code up into small functions and this is a good thing. It makes it easier to test and it makes it easier to reason about. It also lowers the cyclomatic complexity
At the moment it is not possible to configure JSHint to disallow braces for loops and conditional, but it would be a good thing.
There are other cases where braces appear in Javascript most notable when creating new objects. I could argue that object creation should also be moved to its own function but, I currently don't follow this guideline myself.
30 comments:
This is not braceless because if you actually count the braces, the count is exactly the same. This is a refactoring call "extract method" and yes the code is cleaner and easier to understand but this has little to do with the braces count.
@Julien, you are right about the absolute count, but since I am not counting the braces that I cannot avoid, it is a win.
In essence, I am replacing unnecessary braces with necessary ones and this has the side effect of making the code cleaner.
Couldn't resist.
A re-telling of "Braceless Programming" - https://github.com/pmuellr/animals.
@Patrick, I agree, very nice
If you don't like braces => switch to Coffescript :)
So much cleaner and more readable :)
@Torkel, Yes, I like Coffeescript too.
My Coffeescript style is similar.
feedAnimal = (animal) ->
if animal.isHerbivore()
feedHerbivore animal
else if animal.isCarnivore()
feedCarnivore animal
this is one of the reasons to use CoffeeScript. I agree that jslint is too restrictive in a lot of cases, but the braces aren't one I've had a gripe about. I can't see pulling out all multiline conditionals and loops into their own functions being scalable. You end up replacing the ugliness of braces with the ugliness of more functions. Sure, your code is then easier to break into isolated testable chunks, but it doesn't seem practical.
But then, I had a similar reaction the first time I looked at CoffeeScript. Time will tell.
I absolutely 100% agree that "Variables should be declared on first usage" is terrible and is the single reason I would never even consider using JSLint.
The benefits of doing that are only noticeable if your functions are so big that you have much bigger problems to deal with anyway.
And the drawbacks are you are putting related code in different places, a readability and maintainability headache. JSLint requiring this is what I'd call a "wrong opinion".
Less strongly, I disagree with both JSLint's opinion and your opinion on braces. I also think braces are ugly, so they should be omitted when *syntactically* not reqiured (one-line conditionals, for example). But I see no problem grouping, say, multi-line conditionals into braces rather than refactoring out to smaller functions. Contrary to your opinion, I think that doesn't *necessarily* correlate to better code.
Oops, in my last commit, I said that wrong it is "Variables should be declared on first usage" is what I 100% *agree* with. I disagree with JSLint's "variables must be declared at the top".
This reads to me as:
* Split your code into small functions (agree)
* Don't use braces for if/else statements (disagree)
I have also referred to this as the Arrow Head Anti-pattern. Either way I agree with you.
@Adam, I'm glad we agree a little bit :)
I currently don't see that more functions is ugliness. If I get too many functions in an object I see this as a sign to break out the functions into their own object.
But like you said, time will tell.
@Anonymous, I'm not saying that you must do this every time. There are valid reasons for not breaking the code out into its own function.
Here is a function that will be slower when using braceless style, but I would argue, that it is more elegant.
// Find smallest element and its index, O(2n)
function minWithIndex(array) {
var min = Math.min.apply(Math, array);
var i = array.indexOf(min);
return [min, i];
}
And unless I see the need for the extra speed, I'm going to stick with this way.
@Pablo, At least we partly agree :)
@Chris :D, I didn't know it had a name.
Doesn't the overhead of functions mean braceless style will be slower?
@Anonymous Yes it will be slower. If I need the extra performance I will make an exception.
Using ES6 arrow syntax you can reduce the braces to just one
https://gist.github.com/b6c2fec6bf2b64870af3
The point where you disagree with JSLint about declaring variables on first usage is a fair point. It is a good idea in some cases, but not necessarily all.
However: Firstly, JSLint/JSHint allows you to switch off specific tests if you disagree with them. And secondly, JSLint is intended as something to aim towards; seriously, I would not want to get hung-up over trying to conform rigidly to its every whim.
But your second case (and your main point) about dropping braces where possible, I disagree with you strongly.
You come across sounding like a Python programmer trying to kick Javascript into using your preferred syntax. Braces are part of JS in the same way that white space indenting is part of Python. Don't fight it; it's not worth it. If you must fight it, use Coffeescript instead of JS, rather than trying to bend JS to your whims.
The refactored end result you've produced is actually fairly good code, but not because you've removed any braces.
It is true that good code should not be too deeply nested, and that splitting bits off into separate functions is a good way to reduce the nesting levels. But this would apply whatever the language, whether it uses braces or not.
@Raynos, I'm looking forward to ES6
@Spudley, I agree that JSHint/JSLint are only recommendations (hints :) and I don't follow them rigidly.
But as to your second point. How can you disagree with it strongly? Code written in braceless style, and some common sense, will be clean and well factored.
I'm, actually, mostly a Ruby and Javascript programmer these days and I love both the languages for different reasons.
I don't consider braceless programming abusing the language, rather I think of it as using the language in an elegant way.
I also agree that splitting code into short functions with minimal nesting is good in any language. I write code this way in Ruby too. But in Javascript I can name it, Braceless Programming.
Crockford's reasoning for never omitting braces is that it can lead to hard-to-find errors if you're not careful.
If you added an extra line or function call to your if statement then it wouldn't behave as it looks like it should (with the else statement underneath you'd get a syntax error without an else statement the second line would always execute). This makes the code difficult to maintain, to pass on to anyone else, and to quickly add bits and pieces (say you were being lazy at debugging and wanted to add a console.log() under the if statement to see if it had executed - the if statement would appear to be always executing).
Sure, you could say that you'll always be careful. But you can't say that of anyone else who might want to do stuff with your code. And even if you're really careful, sometimes we just make mistakes. By always using braces you remove one way that mistakes can be made.
Secondly, by using this style you force yourself (and anyone else that has to use it) to always find ways to make if and loop statements one line long. Sure it's always possible, but it's not always obvious, so why limit yourself? If you say, "Ok, well I'll use braces sometimes", then you may as well be consistent and use them all the time.
I'd rather have code with a few extra braces if it massively reduces the likelihood of error.
(Anyway, I wish we could all stop arguing about how "pretty" code is and instead talk about good code. Your functional style is good code - it separates things out nicely and is easily testable. But that's got lost in an argument about whether to include a little curly thing every now and again. JavaScript is an ugly language to look at. That's just how it is. It happens to be a fantastic language to program in though.)
@Mark I'm well aware of the mistakes that can be made if you are mixing this style by sometimes using braces.
But I disagree that we should limit ourselves because other people may make mistakes. By that reasoning we should all be using statically typed languages that protect us from ourselves, and yet we don't.
I like my style better because I think it looks good, it encourages me to make my functions short and understandable and, most important, it makes me happy.
When I am happy I am more effective and creative, resulting in a better program overall. That is not a bad outcome for simply omitting some braces.
I agree with Spudley, you should not bend JavaScript to be something else!
If you really want to omit braces use CoffeeScript, the braces are there for a reason. JavaScript is loose typed when its comes to spaces, and removing the syntactical meaning just so you can omit braces is just plain stupid and dangerous!
Ruby and Python don't use braces for blocks, but they use spaces. I a language that does not use this type of syntax you should use it as replacement as its very error prone.
"Write it in the language you are writing it in." - Douglas Crockford
Its almost like most of Ruby an Python users have this arrogance towards other languages. Even to the extreme by telling that HTML is bad (WHAT?!).
"Variables should be declared on first usage."
Yes, and there is a reason for that (which I hate btw), JavaScript has this strange tendency to move all variables initializations to the top of the function (as undefined), and in some cases (closures for example) that can cause some vary nasty bugs. As you expect that when the variable is encountered is initialized (but its not).
"And unless I see the need for the extra speed, I'm going to stick with this way. "
I'm not even going to reply on this, its just stupid...
To conclude if must omit braces, use a language that does not use them.
And don't trow away speed over own taste.
@Anders
But JavaScript is a loosely typed language, so there's nothing, within the confines of the language, you can do about that.
You can help to stop non-obvious errors simply by adding curly braces though. Maybe not for you, but for anyone else that wants to read or use your code.
It might make you happy, but it could easily make someone trying to use your code miserable: because they haven't noticed and can't work out why an error is happening; or because they have to use your "one-line block" style of code; or because they have to go through inserting curly-braces.
@Mark I guess we just have to disagree. When I am writing code in braceless style the methods are very seldom longer than 4 lines. That makes the code quite obvious. The omitting of the braces lead to this, and that I think is good.
@Sebastiaan, I also enjoy Coffeescript, but I am still using Javascript most of the time.
Ruby does not use whitespace for blocks. You can use braces or do/end.
"Write it in the language you are writing it in." - Douglas Crockford
Yet, right after he says this, he goes on to define a part of the language which he calls the good parts...
I do the same thing, but with a different take on it.
I am certainly not arrogant about Javascript, it happens to be one of my favorite lanugages.
I will choose clarity over speed, anytime that speed is not required.
I agree with the approach and need to make the code as clean as possible (regardless of total {} used), essential in larger projects where code moves between teams & developers.
My preference if to use shorthand for if statements such as your case
animal.isHerbivore()
? feedHerbivore(animal)
: feedCarnivore(animal);
And if there are more than 2 scenarios than declare the value of the animal and use a switch statement (in this case this is handled by the isHerbivore function).
@Andy, I usually reserve the conditional operator for functions that return values.
Looks like a great way to alienate contributors!
Given the behavior of variables in javascript, declaring them at the top of the function helps to clarify how all actually works (search: scoping and hoisting)
As for the braces, breaking the code up into small functions may be useful in other languages, but in javascript, there are closures, and doing so would reduce the ability to compose behaviors, this is not ..., this is javascript!
Post a Comment