Good man. This is the sort of irrelevant detail beloved of pedants. It does not make you a better programmer to know this sort of thing.
You probably should know your language's idioms, but this sort of detail is not useful. I'd rather see a few extraneous parens around things that the compiler can happily remove. This sort of attitude is why my Perl code tends to be readable. There are all sorts of shortcuts and simplifications if you know the edge cases of the language, but I'd rather have clarity than cleverness.
This is the sort of irrelevant detail beloved of pedants.
Pedants, and people who have to read other developers' code. Good lord, man, are you able to retroactively dictate coding style to everybody who wrote any code you have to read? Lacking that ability, I have to know the idioms used by other people who code in a language, not just the idioms I personally approve of.
That's a beautiful piece of code, and really interesting too. It's a great example of how to document what the code is intended to be doing rather than documenting what it's actually doing (eg: "add one to i"). I would maintain your code any day.
Thanks. If I expect a piece of code to survive any length of time I try to document it well because I tend to forget how/why I decided to do things. In that piece of code, I knew that I'd forget the trig. part quite quickly so I figured an ASCII diagram was needed.
Awesome! I hate seeing people say "Make good comments, not like X". I have given up on asking "what would be a good comment" because no one seems to be able to point to a good example. This will be much helpful :).
Really? So if a fundamental assumption changes in the code you'll be happy to make a 3 line change and rewrite 10 pages of documentation inline?
Personally I like the code to be as self documenting as possible about the "what", the check ins to be as explicit as possibly about the "why" (and "who" and "when") and documentation for architectural information ("where").
While I agree with your second paragraph, I have to say that yes, I'd be happy to rewrite inline documentation to reflect a 3-line code change. Ten pages of inline documentation is a lot and probably inappropriately located, but if it really takes ten pages to describe what three lines of code are doing, there is just no way those three lines of code can be self-documenting. (So much for extreme examples...)
As you mentioned, there are different places for documentation: inline, commit comments, change tracking system, project/release planning documents, and overall system architecture documents. These all serve different purposes, describe different aspects, and typically have different audiences. They all have different levels of detail too. The code itself is the most detailed documentation, because it describes what the software actually does when it runs, and should be as readable as possible. The inline comments supplement that, because the code describes "what" but not "why". You mention that check ins should describe "why", but they give the why for the overall change, not for each block of code. Inline comments give the why for each block of code.
I mostly do this, especially for mathematical expressions, but in some cases languages have developed idioms where the common and readable style is to leave out some parens. It's dumb to do it just as some kind language-lawyering way to save chars, and it does raise the amount of background knowledge about the language you need, but putting parens everywhere can really break readability as well.
For example, this is idiomatic and readable C (ignoring whether it's a good idea to write this specific string-copy loop these days):
while (*dst++ = *src++);
whereas this is line noise that you have to decode:
while ((*(dst++)) = (*(src++)));
The first form does require you to know that ++ binds tighter than the dereferencing operator, so the increment is incrementing the pointer, not the value it points to. But it's easier to scan, and used enough that people who code in C presumably can just glance at it. It makes a nice syntax pun due to the interaction of the order of operators and the postincrement, too. You can read it as: deref dst, then increment dst, which gives the nice intuition that the deref happens, and then the increment happens. The parenthesized version is more confusing on that point, and you'd probably just want to rewrite it into a more verbose loop with 3 separate body statements, if you were going to go that route.
Heck, even Java does this regularly. Method-call dot-chaining is idiomatic in Java, even though it relies on an evaluation rule to work properly:
foo.bar().baz().qux()
Not idiomatic:
((foo.bar()).baz()).qux()
For the same two reasons basically. The fully parenthesized version is too noisy, and the unparenthesized version has a nice syntax-pun interpretation: it's a "chain" of results going down the list of methods.
It's not just that it's too noisy. One wonders on reading it whether the parens are there to override the default precedence. I have the feeling that there's a trick going on in there and I have to look much harder to verify that it is normal.
I'd just like to point out that in your string example, you gain nothing by keeping that string copy short. The compiler doesn't see lines, all it sees are tokens, and what you've written there is fine for a compiler to read, but difficult for a human.
Instead of adding parentheses, try writing it like this:
while(*dst)
{
*dst = *src;
dst++;
src++;
}
It's instantly more readably, it's very obvious what's going on, and you've lost no performance. There's also no parentheses to add.
I actually find that style considerably less readable, though admittedly I'm quite familiar with C code. It's just less common for one (not the idiomatic way of writing code in the language), but it also emphasizes this tedious loop-iteration-at-a-time interpretation, while the original is easy to read as a sort of Matlab-style single vector operation, once you're used to it ("copy all the srcs to all the dsts").
Plus, once you start piling on a bunch of such code-explosions, you end up with functions that are much harder to scan, because what could've been an easy to scan 8-line function is pedantically written out as this super-explicit 30-line thing.
Agreed. I think the rule is to understand what is obvious and readable and what is not. Doing "var++" is very readable, as are method calls, specifically because they're designed to be used in this way.
I'm 100% with Tim here, I've never felt the need to memorise the order of precendence for operators - as long as I've got my parens I'm happy. Luckily I seem to work with others who feel the same way. If we did have someone insistent on pushing the boundaries of readability I'd spend about a minute looking up the precedence rules then another few weeks framing the perpetrator for some real-life infraction as a form of punishment (depending on the particulars this could range from petty theft all the way through to light to medium treason)
I would draw the line at additive, multiplicative operators generally, but I would also include &&, || and comparisons in C-derived languages (contra Tim). Knowing the extra three precedence levels is a cheap investment that covers such a wide range of commonly used languages - C, Java, C++, C#, bash's let/for/etc.
I don't think it's necessary to have to parenthesize:
u*t + a*t*t / 2.0
Working with Pascal (Delphi) quite a bit as I do, I also find this idiom - required in Pascal with its limited number of levels - tedious:
if (a > b) and (b < c) then // ...
The trouble is that you begin writing this:
if a > b then
but updating it to add in the new conditional means you have to go back and forth over the expression inserting parentheses. I think C and related languages at the expression level make this better by making comparison operators have a higher precedence to boolean operators.
Bitwise operators, of course, have an entirely different precedence level and if you're using C, it's easy to get caught out:
if (flags & MASK == FLAG1 | FLAG2) // cue much confusion
A related case: I have never bothered to learn, nor will I, what the default modifiers for Java’s variables and methods are. Thus, every one of those things gets one of private or public or protected in front of it. There are way better things to load up the finite cupboard-space in my mind with.
Oh, no, man, that's not good! "Default" visibility (package-private, i.e. visible within the same package, but not outside of it) is there for a reason, and that reason is that it's not the same as public, private, or protected!
There's literally no way to specify a package-private variable without leaving the modifier off, though some people (including myself) like to write
/*package-private*/
as the visibility, just to be explicit.
Aside from that nitpick, I agree with you, though, when in doubt, be explicit, it will save you a lot of trouble in the long run.
"I have never bothered to learn, nor will I, what the default modifiers for Java’s variables and methods are"
That means that he doesn't understand "package" level access. There is no "package" keyword (well, there is, but it means something else). To get package-private access you have to leave it unspecified, so that means he can't use this feature. Or grok it when he comes across it.
The lack of a keyword for this is a shortcoming in Java, IMHO. But even if you don't use it often, you should have an inkling of how it works.
Most of my classes are package private. It is just a matter of information hiding. Especially when using factory patterns I hand out only public interfaces whereas the implementations are kept package private.
Exactly. To elaborate a bit for those who never used it in that way: you want to separate your unit tests from your app code. But you still want to give your tests access to non-public methods in order to test them independently. The solution is to put app code and tests in different folders, but in the same packages, and make everything you want to test package-scoped.
The problem is that Bray's reasoning could apply just as well to any aspect of a language that can be worked around, and leads to meaningless bloat in every case. Other examples:
"We shouldn't have to learn the standard library. Always implement functions you want yourself, and comment what each line of code does."
"We shouldn't have to learn about variable scopes. Just put all your variables at the top of the file."
Knowing the basics of operator precedence should be expected. If you don't know it, you don't really know the language. Bray even drops his own thesis when his code sample assumes that the dot operator and function application operator have higher precedence than logical-and.
There is definitely such a thing as expecting too much familiarity with operator precedence, but it's illogical to say that you shouldn't need to know anything. These are the syntax rules of the language, people!
> Knowing the basics of operator precedence should be expected. If you don't know it, you don't really know the language.
What are "the basics of operator precedence" for C++?
Yes, I'm serious. I don't think that there's any agreement on that. At best, there are a bunch of answers with significant overlap, and each non-trivial organization has at least two answers.
That's why every non-trivial organization produces software which uses multiple definitions with blurs between them for interfaces and common code. And bugs.
C++ has almost 30 precedence levels and yet folks think that lisp is unnatural.
Regardless, C++ seems to show that "Knowing the basics of operator precedence should be expected." is unreasonable, at least wrt C++.
Note that C++ isn't all that complex wrt precedence, so the real problem is with the claim. Humans can do about 10 levels of precedence. Since demonstrating precedence knowledge is not the point of a programming language....
Knowing the basics of operator precedence should be expected. If you don't know it, you don't really know the language.
I don't see any validity in a comparison to knowledge of the standard library, or of variable scoping. Ignorance of those aspects of the language has very real, measurable costs. Using unnecessary parentheses incurs no cost at all unless taken to an extreme like some of the examples posted above.
Then Smalltalk is perfect for him, with no concept of operator precedence!
Kidding aside, this is smart. Defaults are things we shouldn't have to learn. I use parens for anything they didn't drill into my head in grade school (but in the aforementioned Smalltalk that doesn't work).
The simple parsing in these languages means that none of them supports normal mathematical notation. But it also has benefits when it comes to extending their syntax and building DSLs.
I don't remember all the rules, but I do remember that comparisons come before logical operations. It just doens't make sense to let a < b && c mean a < (b && c).
Perhaps an IDE/editor feature that removed unnecessary parenthesis could clean up code and you'd learn the precedence as you go.
The clearest statement of this I have seen is from the O'Reilly book Practical C Programming : "* and / come before + and -. Use parentheses for everything else."
My general rule, on the rare occasions that I actually write code, is the same as for other writing - write to be easily understood by potential readers. You should take a little time and thought to make it easier for someone to follow your work.
Has been advocated before, notably in the Practice of Programming:
"Parentheses specify grouping and can be used to make the intent clear even when they are not required. (...) Seasoned program- mers might omit them, because the relational operators (< <= == ! = >= >) have higher precedence than the logical operators (&& and ||). When mixing unrelated operators, though, it's a good idea to parenthesix. C and its friends present pernicious precedence problems, and it's easy to make a mistake."
Amusingly, I sent a note to the authors of TPOP when it came out claiming that I had found a precedence error in some of the code in the book, between && and ==.
Java does not, as yet, have a kind of method or closure reference type, so this ambiguity doesn't exist - the parentheses on the method call are not an operator like in C++.
This is an example of a good general principle for communication: don't express your ideas in a way that a reader cannot understand without referencing an external source of knowledge if it is a reasonably trivial task to embed that knowledge into your content in the first place.
Neither do I, for the exact same reason as Tim. Why bother relying on precedence rules when your code is clearer if you use parenthesis? Even if you know the precedence rules, there's no guarantee someone maintaining your code will.
I agree with him that it's good to include those extra parens in code you write. But if you're reading or debugging code you didn't write, it's important to know them. If you just assume the code means what it appears to mean, you'll skip right over even shallow bugs.
I haven't read any of Tim's code, so I don't know how readable it is. But I found that spending a lot of time reading other people's code dramatically improved the readability of my own code. I venture to guess that his code is a little opaque in places without meaning to be, as a result of his not spending much time reading other people's code.
> I found that spending a lot of time reading other people's code dramatically improved the readability of my own code.
This I wholeheartedly agree with. I spent the first five years of my career porting large hairy codebases from PCs to embedded devices, without the original programmers available, and that has given me a drastically different code style to a lot of people.
> I venture to guess that his code is a little opaque in places without meaning to be, as a result of his not spending much time reading other people's code.
Here you lose me. I went out of my way to avoid memorizing the operator precedence rules too well, so that when I saw a complex one-line expression I'd have to stop and pay attention. A lot of the time the subtle bugs creep in because the naked expressions flow in a way that looks seductively correct, and so with an easy knowledge of precedence it's easy to make the same mistake as the original author.
As Bjarne S once said about casting in C++, I want it to look ugly in the code because it's an ugly idea. I want my code-reading to stumble when I hit those long unparenthesized expressions, to make sure I pay attention.
There are a lot of things you don't need to know in order to use a language fairly effectively: you can always look up syntax & things like operator precedence. While that is a good strategy when you need to switch languages frequently, it also means you'll never be an expert in language X. Which, for plenty of programmers, is a good thing.
It's readability vs. clutter - I think most programmers would allow multiplication over addition without parens. It's logical to allow && over || since that's basically the same thing as * over +.
The one that always gets me is x == y&1, which is evaluated as (x==y) & 1.
I know the rules for C (which are the worst of them) and yet I still use parens all over the place. It's less brain burden to read, and if I want to copy and paste it to javascript which has fewer precedence levels, it doesn't break.
Same here, but I find that questions on precedence often pop up during the interview process and the interviewer wants to know details and he is not satisfied with the parens.
Yes, that's a bad interview question. I guess they do not care about you knowing the precedences, but just use it as an easily tested proxy for your general knowledge of the language.
I had interviewers ask about compiler flags. That was even more dreadful. I said I knew Haskell, not that I knew GHC. (In the end, I could answer the question, but they felt so wrong.)
You probably should know your language's idioms, but this sort of detail is not useful. I'd rather see a few extraneous parens around things that the compiler can happily remove. This sort of attitude is why my Perl code tends to be readable. There are all sorts of shortcuts and simplifications if you know the edge cases of the language, but I'd rather have clarity than cleverness.
e.g. my Perl coding style: http://blog.jgc.org/2010/01/more-fun-with-toys-ikea-lillabo-...