Portrait of a Noob
steve-yegge.blogspot.com
steve-yegge.blogspot.com
"It's denser: there's less whitespace and far less commenting. Most of the commenting is in the form of doc-comments for automated API-doc extraction"
This is right on the button. I prefer writing/reading an overall README.txt which says concisely what that module/package etc. is supposed to do and doc-comments where appropriate when something is not obvious. Anything else and i automatically start seeing that as noise filtering it as i go along. The worst are the obvious and/or the outdated ones.
If you have documentation about the design of the system and write reasonably clear code, you can document sparsely. (Having fewer comments also gives those present added emphasis.) As with most engineering, it's more about trade-offs than hard-and-fast rules, though.
And Haskell, OCaml and their ilk are part of a 45-year-old static-typing movement within academia to try to force people to model everything. Programmers hate that. These languages will never, ever enjoy any substantial commercial success
Hold on a minute...last time I checked OCaml was a dialect of ML. ML does not ask you to "statically" type everything, in fact it does the opposite. It infers all the types. Sure, it allows you to hint types to the compiler, but this is not necessary. You get all the benefits of "static" type checking through ML's concept of type inference. It's designed specifically to make it less work for the programmer to enjoy the benefits of a sound, correct type system.
If you want to learn more about type inference in ML, check out this short introduction: http://bit.ly/8WEhD8
When you understand your code, you don't bother to read the comments. As soon as you forget how the code works, you look to the comments for direction.
But if you didn't revise your comments along with your code, the comments will trick and deceive you until you realize that they're out of date. So you must re-grok the code anyway.
Use comments to describe your code's purpose. Let your code self-document the implementation.If you are particularly clever in your implementation, add a quick comment to explain your cleverness. When you realize you shouldn't have been so clever, be sure to remove the implementation comment as well.
Recently, however, I've been getting into Haskell and in doing so I found my opinion on static typing left a little beaten in the legs and suffering some obvious facial wounds. Static typing works in Haskell, I'm pretty sure of that. The question I'm left with is, why?
I think it might have something to do with the fact that type checks in class-based OO languages don't even begin to insure the program correctness that novices are often foolish enough to think they do. Everyone else recognizes the need for all manner of testing, and that's how correctness is proved (albeit, approximately) in imperative languages. In Haskell, I've found that type checks go quite some way to proving correctness, no really! (They don't actually prove correctness but proofs of correctness would be impossible without them). Without even bothering to do any proofs or testing you get a much more rigorous check for correctness in Haskell than you do when a Java program successfully compiles.
I can't emphasize this difference enough. To me static typing in Java is an annoyance and a lie. And so when I program in a language that teeters between static and dynamic typing, such as PHP, I go out of my way to write libraries that make things more dynamic (I've got one GitHub) and I call men who advocate aforementioned type hinting, girls names. In Haskell, it's totally different; it actually works; it's actually useful. Not to mention the clever stuff that you can do with types which I couldn't begin to do justice here given my inexperience.
I too once thought static typing was nothing more than useless bloat, but then I started using Haskell. Haskell's type classes are actually a useful means of handling abstraction (know something that acts like a monoid? then save yourself some effort and use foldMap). The one thing I did like about Java's class system was interfaces, and Haskell's typeclasses handle that exceptionally well.
Also, Haskell is more strongly (and richly) typed than Java\C++\C# so having strongly-typed code works really well.
About proving correctness, I guess you may have seen this amusing piece: http://perl.plover.com/yak/typing/samples/slide030.html
The type system found an infinite-loop bug in the code at compile-time.
The Lisp function Yegge gives, is horrible in that aspect. for example this piece of code:
(if (or (= tt js2-LB) (= tt js2-LC))
should really be more like (if (matches-js2-line-ending current_token))
No matter "how good you are" code reading is faster if what you read matches with what you're doing in English.P.S. The most frequent and flagrant was calculating array indices into multi-dimensional arrays in C++. It's so easy to write a simple class that lets you do this:
arr(n1 + i, n2 + j, n3 + k) = calc_foo(i, j, k);
instead of this: arr[n3 + k + (n2 + j) * (nz + (n1 + i) * ny)] = calc_foo(i, j, k);2) Why not
arr[n1 + i][n2 + j][n3 + j] = calc_foo(i,j,k)
? It's a bit more work to write the proxy classes, but a Sufficiently Smart Compiler(TM) can make that all go away for you.That said, this does require some taste. It is horrible to spread a single logical operation across multiple functions, classes, and files just for the sake of "object orientation" or "self documentation", but judicious application of bottom-up FP really helps code size and readability.
I once had the pleasure of rewriting an Ada avionics subsystem in C. The code was very tersely commented, and the only documentation I could find on the subsystem was a requirement to the effect of "such-and-such subsystem shall exist". So I was left to figuring out what the subsystem worked from the code itself.
That's possible to do, but things would have gone a lot faster if I would have had a few pages describing what the goals and overall architecture of the software was.
In the best case, the design documentation is updated as the code is written and problems are found. However, even then, decisions made while coding will avoid problems, and ambiguities in the design documentation don't get updated when that happens.
I've had to do similar rewrites in the past, and I usually just skim the design documentation to get some idea of what the plan was. However, programming languages are in general the most concise and unambiguous way to represent what a program does.
If you're a n00b, you'll look at experienced code and say it's impenetrable, undisciplined crap written by someone who never learned the essentials of modern software engineering. If you're a veteran, you'll look at n00b code and say it's over-commented, ornamental fluff that an intern could have written in a single night of heavy drinking.
I have never seen this so succinctly expressed before.
"To be honest I'd sit there and say both were equally shoddy and unlearned in their own way. There is a sweet spot middle ground that the really really good programmers learn"
I suspect he is right.
I still don't write much comments when doing hobby programming at home (after all, the whole point of hobby is to indulge yourself), but there is no slightest doubt in my mind that abundance of comments is a good thing. I've never really seen code I considered overcommented. I see code which is severely undercommented all the time.
Edit: It got less inspired as I continued to read but the initial idea was good.
I want to say I read that entire article, but I only read the first few paragraphs, so, my comment may be off topic somehow. That was a hefty post!
Anyone else catch the irony that this essay was really long?
My favorite code has consistently been sparsely commented (though effectively heavily commented with good variable names and appropriate functions) while the absolute worst I've maintained was code that was so heavily commented that it was virtually impossible to see the flow of the program.