Vim plugin to disapprove deeply indented code
github.com
github.com
Seriously though, it's super hard to avoid deeply-indented code in some languages (cough Java, JavaScript).
@Override
protected void onDestroy() {
super.onDestroy();
if(someCrap != null) {
PendingResult<MessageApi.SendMessageResult> pending = Wearable.MessageApi.sendMessage(mGoogleApiClient, mPhoneNode.getId(), path, data);
pending.setResultCallback(new ResultCallback<MessageApi.SendMessageResult>() {
@Override
public void onResult(MessageApi.SendMessageResult result) {
runOnUiThread(new Runnable() {
....Though that could be dangerous.
Although you could just have a build step that waits for a human if the formatted doesn't match submitted.
But generally, if the code is too onerous to modify due to e.g. excessive use of closure-captured data, then the code usually deserves a long, hard ಠ_ಠ to make sure it doesn't deserve a (╯°□°)╯︵ ┻━┻. Hard to test, hard to fix, hard to control.
Truly an idiom for our age.
if(someCrap == null) {
return;
}
I like that Swift makes this more explicit with the guard statement, but I've found it useful to reduce the "mental stack" in other languages, too.In the embedded world, I imagine it would be a good standard to force people to consider refactoring functions to their truth table.
Generally, I strongly favor coding standards to combat bad coding habits. Curious how MISRA works out in practice.
[1] https://spin.atomicobject.com/2011/07/26/in-defence-of-misra...
WTF?? No it isn't. You have this magic initial value and have to figure out which code paths have mutated it and which haven't. Mutable variables make code far far more complex to read.
I'm all for single return but through the use of expressions, not variables; I would probably express the given function as:
expression() {
return
a ?
(b && c) ?
2
:
1
:
0
;
}
(Ideally I'd use a language that allowed a sensible conditional expression rather than one that relies on bizarre symbols, but we work with what's available) let expression a b c = match (a, b, c) with
| (true, true, true) => 2,
| (true, true, _) => 1,
| (false, _, _) => 0
I like how pattern matching allows you to create something very close to a truth table. It allows you to visually see what's going on.EDIT: Just realized that the two code examples in the parent's link don't even do the same... Look at the truth table, and look at the case of 1 1 0. The first code would indeed return 0, but not the second one. Derp.
It's not only a "good standard". It helps with verification: you can verify that the truth table is correct, and then that the function correctly implements that truth table. At least the former can sometimes be done formally.
> Curious how MISRA works out in practice.
Like every other standard, it depends on who's implementing it and with what tools, but it generally works out pretty well.
int multiRet() {
if (!a) return 0;
if (!b) return 1;
return c ? 2 : 0;
}
The singleRet function, as someone else already pointed out, actually has different behaviour (as well as being, in my view, just slightly harder to read and reason about): int singleRet() {
if (!a) return 0;
return (b&&c) ? 2 : 1;
}
Both of these seem to me much much clearer than the multiply-nested versions there, and much clearer than introducing a new variable for the sake of a single return point.(The other way to get a single return point here is to turn the code into a nest of ?: operators. I claim that will be less readable and more error-prone for most C programmers.)
I can't recall any instance I've ever seen where code became simpler or easier to follow as a result of turning multiple return points into a single return point.
Java has better much better code safety than C (no pointers, gc, etc.) is portable via a VM, and is not used in embedded systems. It's also not specifically ISO C. MISRA was built do address certain issues with a certain language, in a certain environment. You shouldn't apply everything it recommends blindly to another language just because they both have conditions and return statements.
If you can explain to me why java benefits from having a single return statement, I would consider it. Otherwise, I refuse to blindly follow guidelines that were designed for different problems.
> None of the points raised in this thread pertains only to one programming language.
Except you replied to a java problem with a C style guide.
> I'm sure you'll find at least one style guide forbidding multiple returns for every language that has return statements.
And if that guide can explain why java benefits from having a single return statement, I would consider it. Otherwise, I refuse to follow guidelines that were designed for different problems or cosmetic reasons.
connection = establish_connection_given_these_conditions(cond1, cond2)
Vs the many lines of checking, creating the ConnectionFactory, passing in the the conditions to the ConnectionConditionFactory, and generally having a new statement every time you want to blow your nose. What were we doing again? I dunno.
I've used it. I'll defend it. It's better than comments when done properly as above.
More generally, functions are not only about abstraction, but also about decomposition.
The only thing that may hurt readability is when the decomposition itself is too much nested. Usually, it's best to split a large task purely linearly in subtasks (functions), and have them called all from a single function. That main task function gives you not only an overview of all sub tasks, but also about the dataflow between them.
In constrast, too much subtask nesting (subtasks of subtasks of subtasks) often hide the overall flow, which then makes it hard to get the overall view.
Having done some debugging where identifiers/comments/structure are misleading I am a bit afraid of such code. I am really not sure what is the best way to structure highly imperative code though, because every option I have seen contains some dark corners.
Such conditions happen all the time, but are unproportionaly painful to debug in bulldozer code :(
Rant: Even though tests are nearly invaluable, though I personally believe that tests written by the same person writing implementation give a bit false sense of security. I see spec, tests and implementation different representations of author's understanding of the problem. If any two of those (e.g. tests and implementation) are produced from the same mind/understanding then they cannot be used to verify that understanding behind each other is the same.
Abstracting away the gruntwork of your connection factory and your connection condition factory into a couple of separate routines - those are resuable bits of code, they'll let you write this code at a higher level of abstraction using reusable abstractions. But pushing the ugly wiring into a non-reusable black box and pretending it's better design - bullshit.
It's best when:
1) The logic is very specific to the situation and isn't reusable 2) Isn't important to the function's api contract.
Then it's not bulldozer code.
Factoring things out into single-use subroutines is a real mixed bag. It's not a clear win at all, not at all.
Arguments sometimes make this practice messy, especially if you find out that your detached subroutine requires 5 different arguments from its parent. But IDEs or smart editors with tooltips or goto-definition make this an almost no-brainer in most cases. Besides that, many languages allow you (or even force you in the case of Smalltalk and Objective C) to solve the argument readability problem at the language level. When it's really naked 'true', you could just use keyword arguments.
I much prefer inlined code that reads from top to bottom, rather to have to jump all over the place into single purpose functions.
Breaking up code doesn't always make it more clear and readable.
The same goes for OOP and desperately creating objects from things that don't represent real world entities.
http://number-none.com/blow/blog/programming/2014/09/26/carm...
if(someCrap != null) {
followed by indentation on the following block, would be turned by the programmer into something like:
if(someCrap == null) return; //nothing to do
without an indent following.
thereby saving the indent and having to have the reader keep track of another level.
How many times have you seen:
}
}
}
}And is it really that great?
Breaking it into line by line is actually easier to follow than nested conditions. (You don't have to worry about proper indentation or matching braces properly, it's simply easier to write.)
I think it's easier to read as well.
if(someCrap == null) {
try {
CrapSurface mCrapSurface = CrapGenerator.generateSomeCrap(CrapGenerator.CRAPPY, new CrapFactory.CrapOptions(new CrapOptionsCallBack() {
@Override
int getHeight() { return 5; }
@Override
int getWidth() { return 9; }
}));
someCrap = CrapSurface.getCrap();
}
catch (SomeException e) {
try {
someOtherFailSafeButInefficientWayToGenerateTheCrap();
} catch (WeirdExceptionThatWillSeriouslyNeverHappenButStupidJavaRequiresMeToHaveATryCatchClauseAnyway f) {
// do nothing because this exception will never happen and if it happens the user deserves it
}
}
} else {
... the other code I had before ...
} if(someCrap == null) {
someCrap = makeNewCrap();
}
That's more readable and less indented. It's also self documenting. } catch (WeirdExceptionThatWillSeriouslyNeverHappenButStupidJavaRequiresMeToHaveATryCatchClauseAnyway f) {
// do nothing because this exception will never happen and if it happens the user deserves it
}
Nice joke, don't do this. I don't believe it's the user's fault, and even so either print the stack trace or name the exception `ignored`. It's not that hard to pipe all ignored exceptions in a single catch.Also, somebody made the conscious decision of making that exception checked, which means it's the implementor's fault for forcing you to catch it. If you want, you can use lombok's `@SneakyThrows`.
It also pushes you ever closer to the dex method cap imposed by Android, unfortunately.
throw new RuntimeException(e);Also, consider using static imports for `Wearable.MessageApi.sendMessage` and `MessageApi.SendMessageResult`.
I needed to a fix a particular kernel protocol subsystem so I could use Linux at work. I sat down with the spec and a non-Linux computer that implemented it. I spent 8 weeks (I thought it would take a couple days--HAH!), and I wrote it and wrote the very nice test suite which included the single test case I couldn't handle (handling would have required major changes to the kernel).
It's a protocol. The word protocol means "state machine". Especially when hardware dudes design something.
However, state machines mean "indentation". One level for current state, one level (at least) for output variables, and a couple of levels for input variables. My code was well documented, tested, and easily changeable if you needed to add a new state.
"But, that's more than two levels of indentation. Rewrite it.".
After I pulled my jaw up off the floor, swore a couple of times, I replied: "No. This code is properly written, well-tested, and very amenable to changes when the spec gets updated. This code does what I need and what a bunch of people need. I will happily distribute it to them and you will have a bunch of users of this subsystem happily telling everybody to not use mainline because they are a bunch of tossers. If you wish to rewrite it to your indentation spec, feel free, but I will not sign off until it passes all of the tests."
So, they did. They decided to rewrite it to remove the indentation. Lots of early returns (full of bugs). State cases that got "simplified" by sharing common code (full of uninitialized variable bugs). And, because of all the sharing, it became difficult to add cases when the spec changed (which it did--regularly).
But, eventually they got their two levels of 8 character indent. After SIXTEEN WEEKS.
My question, of course, was "Why not just pull each state into a function? It was the obvious way to get what you want. It would have been much easier."
The response: "Well, we tried that. But there are so many variables per state, that the function calls were all exceeding an 80-character line length and we had to wrap them too much."
I had a bruise on my forehead that day.
Yeah, formatting rules exist and have good reasons. But please remember that the goal is "code quality and readability"--not formatting rules.
- Is there a way to use an early return? (In your case early return if == null) - How can I get around using an `else` ?
And I am happy this works so well. Of course there are sometimes if/elseif constructs but exclusive if/else is rare.
Often this does involve pulling out blocks of code into a methods that's only called once, but as discussed elsewhere in the thread this is still an improvement in perceived complexity and readability.
0: https://en.wikipedia.org/wiki/Cyclomatic_complexity 1: https://github.com/bbatsov/rubocop
if (items === null) { return 0; }
at the start. Which is fine if that's the preferred style, but if it is, then you should do it regardless of how deeply nested the dependent stuff is.https://medium.com/@matryer/line-of-sight-in-code-186dd7cdea...
It's one of my peeves to be following code paths and oh look, now we're in a situation where it's if (OK) { if (OK) { if (OK) { if (OK) { ... } } } } and I'm trying to remember what all the conditions were to get here since it's long since scrolled off the page. I always find myself thinking "why, why didn't you just return way up there!?"
Spell checks on the other hand are invaluable, especially since you can extend the dictionary to cover new words like "deserialization."
Someone I know used something called Cream, which they said was Vim with a simplified GUI-based interface (I know; what's the point?). But it had one nice feature: If you indented a line, then Cream would draw an unobtrusive vertical line, extending down from the indent's left-most character (I forget how far the lines extended or what that logic was, but they went far enough to be useful). You could easily see how many tabs 'in' you were and line up a current line with one (not directly) above.
I also use the following options in my vimrc:
" set indentline style
let g:indentLine_char = '│'
let g:indentLine_color_term = 66
let g:indentLine_color_gui = '#4f5b66'
" fix performance issue with long lines
let g:indentLine_faster = 1Might go crazy if using this while developing though.. (stop harassing me, EDITOR)
In the extreme, with pure text editor, you want all details together where you effortlessly see them. Helper function means manual search and scroll.
But then vim gets heavy like an ide... Pick your poison.
It is really weaker with sublime or vim, at least from what I have seen. They often did not found right function even when it was in the same file and there was no ambiguity - leading to harder to read code with deep nesting and loooong functions (that really should have been split into named steps).
It does not do that at all when vim user did not bothered to configure right plugins or did not bothered to learn them.
I would gather that sublime text pushes you towards more focused, tight code, because their auto complete isn't context aware.
To your specific point, helper functions should be as close as possible to the function it is helping. If you have easy "go to function" functionality, some people will slack off on this.
Refusal to use helper fuctions also led to a lot of code repetition in various (especially on click) handlers. When you have bugs from people forgetting to change fourth place where pretty much same thing is done, then the code is bad.
We had deadlines and I really think that everything would be way more effective if we took advantages of ide and generated code that express intent better and is composed of smaller units. Instead everything took longer and complexity was pretty much unmanaged (since they refused to use helper fuctions they never learned how to use then effectively nor how to effectively modularize code - the codebas was fine while small and increasingly unmanageable as it grew).
https://youtu.be/D1sXuHnf_lo?t=2m57s
Slightly NSWF.