There's actually something quite liberating about languages that deny you any clever solutions. Just write code that works and don't worry about whether you could have used <fashionable programming concept X> instead. I found Go to be quite refreshing for the same reason - the standard library is pretty small and the language lacks anything particularly magical, even some stuff that Java has like generics. The end result, however, is code that is very easy to read and write for anyone who learned imperative programming in the last 30 years.
That's a common anti-intellectual fallacy about why people code in higher level languages. High level languages are attractive because they let you write code faster and make your code easier to maintain, and not because they're "fashionable".
>There's actually something quite liberating about languages that deny you any clever solutions
To me, "deny[ing] clever solutions" translates to "this code is going to be a pain in the ass to write because I'm going to waste time fighting the language to get what I want." Programmers learn to avoid unnecessarily complicated code as they gain more experience. Mature programmers should have the freedom to exercise their judgement about what kind of code is appropriate.
Writing code faster is not my problem. The problem is building systems (that work) faster and this need sometimes requires using higher-level abstractions that maybe allow for more modularity, that maybe are more composable, or maybe are safer to use (and I'm specifically thinking here of concurrency, parallelism, asynchronous I/O, etc...). Higher-level abstractions is my goal when learning a new language (this or gaining access to another platform).
"clever solutions" is indeed an unwarranted euphemism that people use. But we are ending up having such discussions, precisely because we aren't defining well what we are talking about. Writing less code is a subjective problem. Not being able to build and/or work with a certain abstraction is an objective problem.
Maybe, maybe not. But at least 2 of the 3 points that you made are not true of JavaScript. Also, the whole "write code faster" has always been perplexing to me. The speed at which you write code is never significantly different between any two mainstream languages to really matter in the end, especially since the lifetime of a piece of software is dominated by maintenance.
And I don't know what you mean by "High level language". Java is a high level language.
>To me, "deny[ing] clever solutions" translates to "this code is going to be a pain in the ass to write because I'm going to waste time fighting the language to get what I want."
There's clever and then there's too clever.
One of the very few bits of relatively solid software engineering that we have is that line count for a given task does matter. Fewer lines written by the programmer to do the same thing strongly tends to yield higher productivity. (Note the "by the programmer" clause; lines autogenerated... well... correctly autogenerated tend not to count against the programmer, which is an important part of doing Java, or so I hear.)
And remember, if this were not true, we'd be programming entirely differently; why do anything but assembler if line count doesn't matter? You might be tempted to thing that's some sort of rhetorical exaggeration, since it sort of sounds like one, but it's not; it's a very serious point. If line counts are actually irrelevant, then we'd never be bothering with high-level languages, which up until fairly recently have the primary purpose of doing a whole bunch of things which, in the end, reduce line count.
(Slowly but surely we're starting to see the mainstream introduction and use of languages that also focus on helping you maintain invariants, but that has still historically speaking been a sideline task and niche products.)
Sorry, but unless you have an architect dissect the problem to exaustion and freeze the architecture after it, no piece of software creates the correct autogenerated code, and those lines still have to be changed by the programmer. Several times.
And if you have an architect dissect the problem to exaustion and freeze the architecture after it, that's already a bigger problem than dealing with all that autogenerated code. No win.
Because I'd totally buy that the number of expressions matters.
But I really doubt actual lines matters much.
Still, I do stick by my original point... if you think lines of code are irrelevent, it becomes very difficult to understand the current landscape of language popularity. A language in which simply reading a line from a file is a multi-dozen line travesty is harder to use than a language in which it's two or three, and that extends on through the rest of the language. I know when I go from a language where certain patterns are easy into a higher B&D language where the right thing is a lot more work, I have to fight the urge to not do the lot-more-work, and this higher level "how expensive is it to use the correct pattern?" is a far more important, if harder to describe, consideration across non-trivial code bases.
1) How do you count "expressions"? is (b+sqrt(b * b - 4 * a * c))/(2 * a) one expression or 14?
2) Assuming reasonable coding style and reasonable definition for what an "expression" is, the variance of the measurement "expressions per line" will be very small - thus, "number of expressions" and "number of lines" are statistically equivalent as far as descriptive power goes.
I don't have a citation, although I do remember this conclusion mentioned in PeopleWare - specifically, that "number of bugs per line" tends to be a low variance statistic per person, with the programming language playing a minor role. I might be wrong though.
But I can offer my personal related experience ("anecdata"?) - when you ask multiple people to estimate project complexity using a "time to complete" measure, you get widely varying results that are hard to reason about. However, when you ask them to estimate "lines of code", you get much more consistent results, and meaningful arguments when two people try to reach an agreement. YMMV.
Expressions are distinct from Compositions, and both influence LOC. I wouldn't suspect that Java software is of generally lower quality than Ruby code on average for example even though in Java you might see a Reader around a Buffer around a Stream instead of Ruby's `open`.
I guess what I'm getting at is what you might loosely call boiler-plate. Java has a lot more boiler plate. Which could easily result in 2X higher LOC. Having worked with more Ruby than the average bear I feel very confident being skeptical of the assertion that Ruby libraries are generally of higher quality/fewer bugs.
I think your last anecdote is more getting into Five Whys territory, and it's probably reasonable to expect a greater degree of consensus then.
Final note: Scala is typically less verbose than Ruby by a fair margin (at least if you leave off imports). Idiomatically usage is also Functional to a significant degree in a way that no Ruby library I've ever seen comes close to. So does that automatically mean that Scala is the superior language? (Well of course it is ;-D, but is that the reason?)
How do you count lines? On unix, "wc -l"; if you insist, sloccount, but "wc -l" is a good approximation.
How do you count expressions? The fact it will take you a few paragraphs to answer (you haven't, btw) indicates that it's a poor thing to measure and try to reason about.
I've done some IO code in C# (mostly WCF, bot not just), and I still think you are playing with semantics as far as statistics is concerned.
Figure out an objective, automatable way to count your "expressions" or "compositions" or "code points" or "functional points" or whatever you want to call it. Run it on a code base, and compute the Pearson r coefficient of correlation. It's likely to be >95%, which means one is an excellent approximation of the other.
And I have no idea what you were trying to say about Scala. I wasn't saying "terser is automatically better". I was saying, (and I'm quoting myself here: "number of bugs per line" tends to be a low variance statistic per person, with the programming language playing a minor role"). Note "per person"?
I guess "expression" seems semi-obvious to me since it's a standard rule in SBT. Variable assignments, return values and function bodies might get close.
val a = 1 + 1
That would be an expression. Instantiating a DTO with a dozen fields, using keyword arguments and newlines between for clarity would be a single expression to me.An if/else with a simple switch for the return value would be an expression for example. A more complex else case might have nested expressions though.
It takes some charity I suppose; one of those "I know it when I see it" things. I don't do a lot of Math based programming though. It's all business rules, DTOs, serialization, etc. So maybe not something that could be formalized too easily.
I guess where I'd intuitively disagree (and would be interested in further reading) is that LOC as a measure just doesn't feel like it works for me.
Considering only LOC to implement a task it's likely: Java, Ruby and Scala in that order (from most to fewest). But in my personal experience bugs are probably more like: Ruby, Java, Scala from most to fewest.
Hopefully that helps clarify and not just muddy what I'm trying to express further.
What confuses me is that you appear to be claiming that fewer LOC should correlate strongly with fewer bugs, but then go on to say that terser is not automatically better (in this context (sic?)). Maybe I'm reading more into it than you intend, but I'm left a bit confused.
Which is a confusing use of the term "expression", since it is very well defined when talking about languages - in fact, most formal grammars have a nonterminal called "expr" or "expression" when describing the language.
Your description, though, more closely correlates with what most languages consider a statement.
Regardless, it's just pure statistics - if you calculate it, you'll notice that you have e.g. 1.3 expressions per line, with a standard deviation of 1 expressions per line - which means that over 1000 lines, you'll have, with 95% confidence, 1200-1400 expressions -- it wouldn't matter if you measure LOC or "expressions".
> What confuses me is that you appear to be claiming that fewer LOC should correlate strongly with fewer bugs, but then go on to say that terser is not automatically better (in this context (sic?)). Maybe I'm reading more into it than you intend, but I'm left a bit confused.
What I'm claiming is that, when people actually measured this, they found out that a given programmer tends to have a nearly constant number of bugs per line, regardless of language - that is, person X tends to have (on average) one bug per 100 lines, whether those lines are C, Fortran or Basic - the variance per programmer is way larger than the variance of that programmer per language.
Now, PeopleWare which references those studies (where I read about that) was written 20 years ago or so - so the Java or C++ considered wasn't today's Java/C++, things like Scala and Ruby were not considered. However, I'd be surprised if they significantly change the results - because those studies DID include Lisp, which -- even 20 years ago -- had everything to offer that you can get from Scala today.
So, in a sense - yes, you should write terse programs, regardless of which language you do that in. If you wrote assembly code using Scala syntax, and compiled with a Scala compiler - Scala is not helping you one bit.
My problem is that the solution in a slightly more expressive language is equally obvious, so I have to fight my own rage at java for its stuttering and clumsy excuse for closures. Fighting the language, on the other hand, is when I try to write a conditional or loop in sh.
This isn't a bad tradeoff though. Elegance means generally gains in maintainability, possibly with lesser costs in the actual development. And thinking about elegance in code is the first step to writing better, more maintainable code.
> For example... should I use map or reduce here, or maybe an iterator, or... oh fuck it, I'll use a for loop. It turns out that the old-school for loop works just as well despite earning you precisely zero style points.
I usually stick with for loops because they are clear. Remember:
elegance is about simplicity and clarity. If you sacrifice either of these, you are reducing the elegance of your code.
And a lot of thought went into Unix, as evidenced by its longetivity and long lasting tradition of its phylosophy. To date it's the oldest family of operating systems and at the same time, the most popular. Anybody that thinks the "worse" in the "worse is better" argument is about not carrying, is in for a surprise: http://en.wikipedia.org/wiki/Unix_philosophy
Even in the original comparisson to CLOS/Lisp Machines outlined by Richard Gabriel, he mentions this important difference (versus the MIT/Stanford style): It is slightly better to be simple than correct.
But again, simplicity is not about not carrying about design or the implementation and in fact the "worse is better" approach strongly emphasises on readable/understandable implementations. And simplicity is actually freaking hard to achieve, because simplicity doesn't refer to "easy", being the opposite of entanglement/interwiving: http://www.infoq.com/presentations/Simple-Made-Easy
"Worse is better" is, ultimately, an argument against perfectionism. Many of the features of Unix could have been implemented in a "better" way, and these ways were known to people working at the time. But it turns out that those "better" options are much more difficult to implement, harder to get right and are ultimately counter-productive to the goal of delivering software that works. We can set up clear, logical arguments as to why doing things the Unix way is worse than doing things another way (e.g. how Lisp Machines would do it), but it turns out that the Unix approach is just more effective. Basically, although we can invent aesthetic or philosophical standards of correctness for programs, actually trying to follow these in the real world is dangerous (beyond a certain point, anyway).
I think that's pretty similar to the OP's argument that, whilst Haskell is clearly a superior language to Java in many respects, writing code properly in Haskell is much harder than doing so in Java because, probably for entirely cultural reasons, a programmer working with Haskell feels a greater need to write the "correct" program rather than the one that just works. Java gives the programmer an excuse to abandon perfectionism, producing code that is "worse" but an outcome that is "better".
I think I know what you're getting at, which is that a comparison between Unix and the monstrous IDE-generated Java bloatware described in the OP is insulting to Unix. On this you are correct. But for "worse is better" to be meaningful, there still has to be some recognition that, yes, Unix really is worse than the ideal. Unix isn't the best thing that could ever possibly exist, it's just the best thing that the people at the time could build, and nobody has ever come up with a better alternative.
> Haskell is clearly a superior language to Java in many respects, writing code properly in Haskell is much harder than doing so in Java
I do not agree on your assessment. Haskell is harder to write because ALL the concepts involved are extremely unfamiliar to everybody. Java is learned in school. Java is everywhere. Developers are exposed to Java or Java-like languages.
OOP and class-based design, including all the design patterns in the gang of four, seem easy to you or to most people, because we've been exposed to them ever since we started to learn programming.
Haskell is also great, but it is not clearly superior to Java. That's another point I disagree on, the jury is still out on that one - as language choice is important, but it's less important than everything else combined (libraries, tools, ecosystem and so on).
However, on the "Better" side, ie. the LISP/Haskell side, the moral of WIB is that time-to-market is hugely important. It's not that the "Better" side was bogged-down in philosophical nuance and was chasing an unattainable perfectionism; it's that their solutions took a bit longer to implement. For example, according to Wikipedia C came out in '72 and Scheme came out in '75. Scheme is clearly influenced by philosophy and perfectionism, but it's also a solid language with clear goals.
The problem is that Scheme and C were both trying to solve the 'decent high-level language' problem, but since C came out first, fewer people cared about Scheme when it eventually came out. In the mean time they'd moved on to tackling the 'null pointer dereference in C problem', the 'buffer overflow in C' problem, the 'unterminated strings in C' problem, and so on. Even though Scheme doesn't have these problems, it also doesn't solve them "in C", so it was too difficult to switch to.
Of course, this is a massive simplification and there have been many other high level languages before and since, but it illustrates the other side of the argument: if your system solves a problem, people will work around far more crappiness than you might think.
More modern examples are Web apps (especially in the early days), Flash, Silverlight, etc. and possibly the Web itself.
C is quite odd in that the programmer is expected to pay dearly for their mistakes, rather than be protected from them. BTW it wouldn't be as much fun if they were protected.
Regarding Scheme, it has withstood the test of nearly forty years very well.
The more I learn about assembler, the more I appreciate how C deals with dirty work like calling conventions, register allocation, and computing struct member offsets, while still giving you control of the machine.
On the other hand, some processor primitives like carry bits are annoyingly absent from the C language.
Use the least powerful tool that solves the problem. A map is less powerful than a fold (reduce), so use that if you can. A fold is probably less powerful than a for-loop, so use that if you can. An iterator is probably less powerful than a for-loop, so use that if you need the more streamlined accessing of elements rather than a fine-grained, indexed loop.
Goto has a problem, it breaks the structure of the code. For does not have this problem. Thus, you should avoid goto, and there is no reason to avoid for. The fact that you knows how to write both changes what?
The most obvious symptom would be with libraries; even though GOTOs, loops, recursion and morphisms are equivalent, if most libraries expose APIs in a different style than your code, you'll have to sprinkle translation code all over the place.
It also makes a difference for static analysis, eg. in IDEs and linters. For example, Iterators might give you more precise type hints or unhandled-exception warnings, which makes that style safer (all else being equal).
Of course, the other point is that there are no such 'lowest common denominators'. GOTO certainly isn't, since Python, Java, etc. don't support them. For loops aren't, since languages like Prolog, Haskell, Erlang and Maude don't support them. Recursion isn't supported in COBOL or OpenCL, and took several decades to appear in FORTRAN. Morphisms require first-order functions, which rules out FORTRAN and C, and until very recently C++ and Java. It may or may not be possible to build such features from the other ones, but even so that's clearly working against the language and causing massive incompatibility with everyone else.
Clinging to particular features like this will only blinker us to the possibilities which are out there. In this case, clinging to for loops implies avoidance of at least Erlang, Haskell and Prolog. These languages have a lot to offer, and are/look-to-become the go-to solutions* in the domains of concurrent, high-confidence and logical/deductive code respectively. Clinging to inappropriate concepts dooms us to 'reinventing the square wheel'.
* No pun intended ;)
PS: I actually think recursion and morphisms are safer, more structured and conceptually simpler than for-loops. See http://lambda-the-ultimate.org/node/4683#comment-74319
In basic you can use your labels and goto statements to create a hierarchy of dispatching routines. And if you're very disciplined about which global variables you use as flags to control the execution flow, i.e. which goto statement gets selected next, and which variables to hold return values, you could write a decent program.
The danger lies in that it allows the programmer too much range to produce low-quality code. The negative effect includes too much attention to the implementation of abstractions, instead of its usage. The existence of the goto statements can undermine other abstractions offered by the programming language.
Goto doesn't break structure of the code, programmers do. (I guess that's the whole reason we stopped using it: reducing the risk of making crappy software)
In my comment structured programming refers to using structured syntax to generate goto statements, so you don't have to see them or implement them yourself. It should free the programmer of considering those alternative ways of controlling program flow. Presence of goto statement points to a flaw in the language design.
To answer your question: Because the basic language I'm referring to, was on my TI84-plus calculator, and it only had an if-statement (no if-else!).
There is a very simple isomorphism between each of the typical control structures (sequencing, choice, and iteration) and its implementation with gotos. It's an easy mechanical translation, in either direction. I don't think Dijkstra was making any claim that spelling these control structures with goto radically increased the difficulty of programming. The important thing was using reasonable control structures (and only reasonable control structures) in the design of your program. Obviously, having the language do it for you is preferred much like any other mechanical translation - but that's not the key point.
"It should free the programmer of considering those alternative ways of controlling program flow. Presence of goto statement points to a flaw in the language design."
I don't disagree with any of that.
"Because the basic language I'm referring to, was on my TI84-plus calculator, and it only had an if-statement (no if-else!)."
That's still an example of using goto to implement missing control structures, not using goto when the control structure you want is present.
For loops have the advantage, particularly in languages like Perl, of being quite clear.
One important thing to note with any language is that the culture matters - a lot. For instance, Tcl and Tk let people easily create GUI's, but there was no UI culture. Most of the main books didn't dedicate even a page to how to make a good looking, functional GUI. You can produce some godawful messy code in Ruby if you put your mind to it, but there's a culture of not trying to be too clever, and also of writing stuff that's easy(ish) to read later.
Since I'm not really a Java guy at all, I don't know enough to comment in an authoritative way, but I sneak a glance from time to time, and my guess is that there may be a bit of a culture of over-designing things.
I think overall these cultures don't indicate too much of an underlying problem and aren't too harmful, except when assimilating developers from other language. But to your point, these barriers to entry likely exist for nearly every language (and if not, other barriers do), so on balance, it's largely a wash.
I think that the Java ecosystem being so massive and Java being so popular, it also attracted a lot of mediocrity, since the usual bell curve applies. That said, I know Java developers that definitely don't like verbosity and that don't follow the usual "best practices" as some kind of dogma. In the end, it all depends on the work environment you end up in.
(I assume that you are not being sarcastic - my sarcasm detector did not go off. In case I missed the sarcasm, apologies).
It is not at all a mystery, if you had been there in the late '90s / early 2000s : Java was actively marketed to management as a language that's usable by mediocre programmers to produce usable results ; "See, unlike C/C++, there are no buffer overflows, no dangling pointers, no undefined behavior, no platform dependencies, no memory leaks - your projects now are in bad shape because of these, but when you switch to Java, all of these problems will be a thing of the past". And those same people who bought the marketing hype, were also mostly believers in the "programmer is a replaceable cog" religion, which is still prevalent today in many places (not just Java), and during the .com days was actually somewhat truthy in the sense that people switched jobs so fast that you had to build your culture in such a way that people are replaceable, because programmer turnover at many companies was exceeding 20%/year.
Today's Java culture is rooted in those days, and it has an underlying "make it painful to not be stupid-simple" directive, and "make it easy to replace the programmer" requirement. Though these are often satisfied, my impression is that overall, it does not make projects more likely to deliver (on time or at all), or higher quality. But it is what it is.
But i think there's another significant factor. Java came as a successor, of sorts, to C++. Early Java programmers were former C++ programmers, and its designers had C++ experience (as well Smalltalk, Lisp, etc experience). As a result, many features of Java are responses to pain felt in C++. Feeling the pain of memory leaks and use-after frees? Garbage collection. Feeling the pain of segfaults and incomprehensible pointer chains? No pointer arithmetic (the party line for years was that Java had no pointers, only references). Feeling the pain of baffling symbol use in APIs? No operator overloading. Feeling the pain of aeons-long build times? Compilation to bytecode, with linking left to runtime.
I believe this pain response carried over into the culture. The pain in C++ didn't just come from the language, but from the fact that people did clever things with it (template metaprogramming had been invented - discovered? summoned? - in 1994). There was a strong association between cleverness and pain. Cleverness was therefore taboo in the brave new pain-free world of Java.
There was another point in the grandparent, and in the original post, about Java culture's tradition of verbosity. I think this is also a post-C++ thing. In C++, it takes a lot of writing to get anything done. In Java, it takes a bit less. You feel like you're moving faster when you write Java. So, as an ex-C++ programmer who has the endurance to write a lod of code, and is feeling the exhilaration of moving fast, what do you do? Write as much code as possible?
There has certainly been a huge shift back towards concision in Java, but it's not something that's completely permeated the community. There are people today still learning Java from books written by people who learned in the '90s and who internalised that logorrheic style. We'll probably never move entirely beyond it.
Maybe the language didn't have powerful tools in its core? What if getters/setters weren't mandatory, like in Scala, wouldn't we write more tiny expressive classes to hold intermediate values?
Think about this example of Dependency Injection: Why did we have to use DI? Because using statics as a dependency injector (like in Play) prevents from unit testing. How could we have solved it? By allowing inheritance override on statics. We don't have it, therefore we have Spring.
Even the Servlet API is criticized for being inexpressive [1]. They've done it so wrong that every single framework reimplements url routing (Struts, Spring MVC...) resulting in those horrible /myserviceaction.do patterns. In turn, those patterns deny RESTful apis, prompting for yet-another-framework for REST resources. We just became Enterprise [2].
I personally have no wonder why we became verbose. But it's understandable, since Java was built using community processes.
[1] http://blog.lunatech.com/2011/12/08/wrong-with-servlet-api [2] https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...
Abstraction has a huge mental cost (I ignore the execution speed cost, because it is mostly irrelevant these days for most projects).
You need more than one case to abstract over. Abstracting "just in case" is, much more often than not, very leaky - that is, requiring you to keep in mind the details of both the abstraction and the abstracted - and requires revision when you actually need it.
Abstraction is not magic, not cost free, although Java culture is to assume that it is both. (Same criticism for C++, BTW; C culture tends to err the other way -- which, in my experience, is the better error to make)
I've been programming in Java (gulp) all my professional life of about a decade. I can totally attest to this.
Just couple of days ago in one of the code reviews I gave a lengthy explanation for using StringUtils.equals("foo", someVariable) over "foo".equals(someVariable). I could have instead just said what you've stated; prefer verbosity over cleverness :)
If code review ends with lengthy explanation over why not use "foo".equals(someVariable) or some other maintenance/readability/effectivity/other important consideration non-affecting issue, I see something wrong with code review.
http://steve-yegge.blogspot.hk/2006/03/execution-in-kingdom-...
StringUtils.equals(a, b) on the other hand clearly states in its interface document that it is null safe and there's no NPE trap.
There, I said it :). That was my lengthy argument for using StringUtils.equals!
Code reviews should focus on more important things than this, IMO.
Making code that shouldn't need to be "null safe" masks errors, and ultimately makes them harder to track down.
I love Ruby dearly, but I can't agree with this. There's a lot of being "too clever" in Ruby land. Not in terms of writing fiendishly convoluted but brilliant code (though we do have a nice subset of "code as art", like _Why's Camping micro-framework), but in terms of caring too much about elegance over things like speed and memory usage, until it comes back to bite us.
My pet peeve at the moment is Bundler and Rubygems, which while they are fantastic productivity tools when you start out, has the serious issue that the way they grow the load path means that the time to require a file grows exponentially with the number of gems. 100k+ failed stat calls on startup just to try to be "too clever" about not making people specify more clearly exactly what they are loading is no fun.
Many of these "too clever" things then starts to dictate app design. E.g. if you want to make a command line app out of a large tool that depends on a long list of gems and Bundler, you better think long and hard about making sure you defer all loading as much as you possibly can, or alternatively start up the app in the background and make a small command line app that talks to it over a socket...
For my part, I have a longer list of other Ruby practices I've come to detest than most, probably, as I'm working on a "as static and ahead of time as possible" Ruby compiler, and there's a lot of Ruby idioms that massively complicates things because Ruby developers are used to the malleability of the language, and so will often alter fairly central parts of the language even when there may be conceptually simpler ways of achieving the same that just doesn't look quite as clean.
Again Rubygems is a great example: It replaces "require". That's one of the means it uses to munge load paths. On the surface it was a great thing: it let Rubygems let people pretend Rubygems wasn't there, and yank out gems in favour of manually installing the files if they wanted to. But it came at the cost of contributing to the load path/stat mess I mentioned above.
Some of these things are in fact great no matter what, and exactly why we love our languages. But some of them come with ugly warts that we grow blind to because we understand the underlying reasons for why things have been done the way they have.
I think all languages grow these kind of warts. To me, Java has more of them than most... Java from beginning seems to have grown a culture of ceremony and abstraction that due to the language easily (but not necessarily) ends up with very verbose code.
http://hop.perl.plover.com/ - seriously clever.
Yes, I know that he says that he is negative and his praise comes across badly (he gives an example of him damning his blogging software). I suggest, if he is being sincere, that the problem is his attitude. If he isn't being sincere, then I'd say that he's not as clever as he thinks he is, or he just never had any passion for Java and so he actually hates it as he never mastered it.
In terms of boilerplate code, I'd love to see his exact examples. With annotations, I've found that plenty of Java code is actually not verbose at all. If he churned out thousands of classes when actually he could have reduced the number of classes to 50, then that's an example of poor management and poor workmanship, certainly not a problem with the language.
Here's the real issue: the article conflates a corporate environment that likes to measure productivity in klocs (or from the number of classes you create) with the tool that is being used. He even admits that he could have actually produced better code, just that nobody cared to correct him when he produced crap quality code.
However, when it comes down to it, the real phrase that comes to mind is:
YHBT. YHL. HAND.
The guy is trolling you all.
(Different languages have different failure modes. With Perl, the project might fail because you designed and implemented a pile of shit, but there is a clever workaround for any problem, so you might be able to keep it going long enough to hand it off to someone else, and then when it fails it will be their fault, not yours. With Haskell someone probably should have been fired in the first month for choosing to do it in Haskell.)
The languages he praises so highly are now put down. Correction: in each case, he's dissing the programmers who wrote the crappy code and caused the project to fail, and he's damning the terrible management that didn't have good enough leadership and oversight to prevent the project failure.
Seriously, this is likely asking proof regarding the presence of sun during broad day light.
>>With annotations, I've found that plenty of Java code is actually not verbose at all.
Again. Are you really serious?
>>If he churned out thousands of classes when actually he could have reduced the number of classes to 50, then that's an example of poor management and poor workmanship, certainly not a problem with the language.
You certainly haven't ready his book 'Higher Order Perl'.
Indeed. And?
No disrespect to the guy though I'm sure he is and general respect for publishing a book like that.
I guess just limited exposure to Java
Why is the age of this book relevant? It's not an overview of the basics. it's a 500+ page exploration of Perl's functional programming features, a topic rarely (if ever) covered in this depth.
Time to re-read it, because when I first read it, I had to skim some parts. Probably applies to Python.
Even the best master carpenter wouldn't be able to build something as simple as an end table if he were given a pickle to use as a hammer, and an onion to use as a saw.
Also, have you heard the saying: A bad workman blames his tools
Well, it sure did a good job of identifying a lack of skill of folks on HN, so maybe it isn't quite so crazy after all.
Apparently you missed his second sentence:
> The first question on the quiz is a triviality, just to let the candidate get familiar with the submission and testing system.
Sure, you can overdesign in any language, but some inner language characteristics of Java lead to it more than other languages do. Copying input to output is a statement, if you think about it in in a logical, human way: "I want you, program, to perform the action of copying this to that". But Java, unlike some other languages forces you to say: "Make a class, make a main function because YOU need it, Java, and only after please do what I really want and copy that input to the outout".
In python, for instance, I'm in control of whether making a StreamCopier class, a copy_stream() function or just get to the core of what i want and write "print(input())".
Generally speaking, in most dynamic languages if I want to access the to_string() method of something passed to a function, I can pass a list, an int, a Duck object and till it has a to_string() function I'm good to go, or I can even monkey patch to_string() and call it a day.
In (too) many cases Java the language forces you to make instances over instances and implementation of interfaces and the like just to access the data you need in the way a framework or a method wants, not you, and often this is frustating because you see your data "just there" and the language fights against you preventing you to acccess it in an easy way. I need to call to_string() of SomeClass but I have an instance of SlightlyDifferentClassStream? Good luck with that, maybe the only possible way is to create a DifferentClassConverterProxy just to have a DifferentClassTranslator, extend TraslatorStream, feed it with my SlightlyDifferentClassStream and have something compatible with SomeClass.
This is not only true to Java, some of these "problems" arise from it being a statically typed pure OO language (a very good thing on my book when it is not implemented in a dumb way) that is put to shame by a cleaner implementation of the same principles like the one seen in C# that, on top of all, also offers a powerful dynamic programming, lambdas and so on.
Also, Java suffers from an enterprisey background. I consider a language environment to be a very relevant part of a language itself, and Java has promoted the proliferation of ridiculous bahamut frameworks with a freaking large number of classes and instances "just in the case someone needs to extend it".
These are facts, not "Har-har-so-many-classes".
Take a sane implementation of a web framework like Django. It is an opensource project born from the needs of a small group of developers. You don't have "so many classes" if you only write those that you really need to solve of your problems, and the project progresses from there to embrace the everyday, real world needs of a larger group of contributors. Most Java frameworks I've worked with really give me the tangible perception of a large group of monkey developers programming them following line by line a technical specification bible of thousands of pages written by a council of architects with business people yelling at them "mooooore, mooooore, we need more of all of this so that we can sell to ANYONE!"
But it's not making a good point. It's not even close. I wish people would stop voting it up.
- "In Java, you can forget about doing it in the cleanest or the best way, because that is impossible." -- cleanest in any language? Or cleanest for Java? Of course the "cleanest" way for Java is possible, and it does matter -- there is a huge difference between "clean" Java and unclean, and anyone who says otherwise is a Java pretender. And arguing about "cleanest for any language" is just a proxy for a language flamewar.
- "And nobody can glare at you and demand to know why you used 576 classes when you should have used 50, because in Java doing it with only 50 classes is probably impossible." -- The "Har-har-so-many-classes-and-don't-even-get-me-started-on-variable-names" argument. In Java, as in any language, you can over-design, or you can under-design, or you can design just what you need. The number of classes may end up higher than in other languages, but this argument is silly and tired, and yes, length is one of the tradeoffs of Java. An IDE (gasp!) should make that largely irrelevant.
- "The code was ridiculously verbose, of course, but that was not my fault. It was all out of my hands." -- This word "verbose" keeps being thrown around as an insult, but nobody brings up the tradeoffs. Verbosity has a purpose, just like brevity and terseness do. And of course it's never out of the programmers' hands. Once given a language, it's all relative.
There's more, but I'm arguing with a person who believes that a question about stdin and stdout is a proper gateway for measuring skill toward any programming problem ever.