Tim Bray doesn't know operator precedence rules
tbray.org
tbray.org
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-...
If you enjoyed that piece of code you might also enjoy the C code linked here: http://blog.jgc.org/2008/02/tonight-im-going-to-write-myself...
Perhaps there should be a 'beautiful code' blog as a sort of counterpart to the Daily WTF. I could reuse the domain usethesource.com.
Update: An there it is UseTheSource (http://news.usethesource.com/news). Submit examples of beautiful or interesting code.
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").
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.
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.
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.
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.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.
Explicit is better than implicit.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 confusionOh, 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.
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.
http://stackoverflow.com/questions/403583/what-is-the-use-of...
"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!
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.
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....
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.
Also, "1 + 2 * 3" is quite obviously 6 and not 9 in languages with operator precedence, I really don't think you need to write "1 + (2 * 3)".
Everything in moderation.
EDIT: hahaha, well I feel stupid. I'm leaving the incorrect answer there because it's too funny not to :)
ghci> let f = let 2*3=5 in 1+2*3
ghci> f
6Obviously ;)
Perhaps an IDE/editor feature that removed unnecessary parenthesis could clean up code and you'd learn the precedence as you go.
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.
"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."
But I had gotten it wrong.
while ((calls.moveToNext)() && (count < howMany)) {
or while ((calls.moveToNext()) && (count < howMany)) {1. type promotion rules
2. method resolution order
3. APIs for simple functions, just write your own--much more explicit.
4. order of evaluation in "for (init; cond; inc)" loops and other nuances of syntax/semantics.
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.
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.
In all other aspects, thumbs up to the post.
The one that always gets me is x == y&1, which is evaluated as (x==y) & 1.
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.)