EWD 340 (Prof. Edsgar Wybe Dijkstra) [1]:
"The competent programmer is fully aware of the strictly limited size of his own skull; therefore he approaches the programming task in full humility, and among other things he avoids clever tricks like the plague. In the case of a well-known conversational programming language I have been told from various sides that as soon as a programming community is equipped with a terminal for it, a specific phenomenon occurs that even has a well-established name: it is called “the one-liners”. It takes one of two different forms: one programmer places a one-line program on the desk of another and either he proudly tells what it does and adds the question “Can you code this in less symbols?” —as if this were of any conceptual relevance!— or he just asks “Guess what it does!”. From this observation we must conclude that this language as a tool is an open invitation for clever tricks; and while exactly this may be the explanation for some of its appeal, viz. to those who like to show how clever they are, I am sorry, but I must regard this as one of the most damning things that can be said about a programming language. Another lesson we should have learned from the recent past is that the development of “richer” or “more powerful” programming languages was a mistake in the sense that these baroque monstrosities, these conglomerations of idiosyncrasies, are really unmanageable, both mechanically and mentally. "
[1] https://www.cs.utexas.edu/~EWD/transcriptions/EWD03xx/EWD340...
"I see a great future for very systematic and very modest programming languages. When I say “modest”, I mean that, for instance, not only ALGOL 60’s “for clause”, but even FORTRAN’s “DO loop” may find themselves thrown out as being too baroque."
While I agree with the general sentiment, it's very important to take anything Dijkstra says with a huge grain of salt. He was a mathematician first and foremost. He obsessed about things like a mathematician. Such people, while very smart, make for very unproductive software engineers. They also have a way of sounding deceptively smart and thoughtful, when really they're just talking out of their ass. Beware.
Without qualification, Dijkstra was one of the greatest computer scientists in our field's short history. While I don't agree with every single one of his ideas, I would encourage budding computer scientists and professional programmers to look over what he accomplished. He most certainly was not unproductive.
Wikipedia says that Dijkstra is known for:
- Dijkstra's algorithm (single-source shortest path problem)
- DJP algorithm (minimum spanning tree problem)
- First implementation of ALGOL 60 compiler
- Structured analysis
- Structured programming - Semaphore
- Layered approach to operating system design
- T.H.E. multiprogramming system (one of the first operating systems)
- Concept of levels of abstraction
- Concept of layered structure in software architecture (layered architecture)
- Concept of cooperating sequential processes
- Concept of program families
- Multithreaded programming
- Concurrent programming
- Concurrent algorithms
- Principles of distributed computing
- Distributed algorithms
- Synchronization primitive
- Mutual exclusion
- Critical section
- Generalization of Dekker's algorithm
- Tri-color marking algorithm
- Call stack
- Fault-tolerant systems
- Self-stabilizing distributed systems
- Resource starvation
- Deadly embrace
- Deadlock prevention algorithms
- Shunting-yard algorithm
- Banker's algorithm
- Dining philosophers problem
- Sleeping barber problem
- Producer–consumer problem (bounded buffer problem)
- Dutch national flag problem
- Predicate transformer semantics
- Guarded Command Language
- Weakest precondition calculus
- Unbounded nondeterminism
- Dijkstra-Scholten algorithm
- Smoothsort
- Separation of concerns
- Program verification
- Program derivation
- Software crisis
- Software architecture
This quote by galactipony is really offensive to me:
> Such people, while very smart, make for very unproductive software engineers. They also have a way of sounding deceptively smart and thoughtful, when really they're just talking out of their ass. Beware.
It is quite clear to me that Dijkstra wasn't "just talking out of [his] ass."
Learn more about Dijkstra at https://en.wikipedia.org/wiki/Edsger_W._Dijkstra or in any resource on the history of Computer Science.
I didn't say Dijkstra was talking out of his ass in this case. I'm saying you often can't tell when people like him are talking out of their ass, as they sometimes do. Who would think the inventor of all these algorithms would ever utter a half-formed thought, on a whim? Unthinkable! Yeah... no.
Another distinction you fail to see is that somebody who is great at finding the best or optimal algorithms (i.e. a great theorist) isn't necessarily a productive programmer. To the contrary, perfectionism and productivity are at great odds.
Dijkstra was heavily at odds with real-world programming as it was done, to the point of isolating himself with his work. If we followed his opinions on how to program, we wouldn't get much of anything done.
I suppose what you're saying here is that there are a lot of business tasks which don't require a great theorist.
I don't think Dijkstra would disagree.
But still, calling Dijkstra an "unproductive programmer"? Really? I'll take one Dijkstra's algorithm over a dozen web apps. And if I can have a patent on it, I can even make a solid business case for that choice.
> If we followed his opinions on how to program, we wouldn't get much of anything done.
And yet, how many man-years of engineering effort could we waste having bad theorists who can quickly hack out LoB code try to re-invent Dijkstra's algorithm?
Perhaps software engineering is a very wide field, and it takes all types?
I've learned this the hard way in my career. I always have to fight against taking the extra 20 hours to perfect something when it only took me 1 hour to get to 95% and 95% is more than good enough for the particular task.
But, lets discuss the 'for clause' and 'do loop': these constructs were made specifically for one kind of simple loop. It is not a systematic solution for an iterative process. To me it seems Dijkstra specifically aims for languages such as LISP (which, with the renewed interest from Clojure is one of the most oldest successful (semi-) functional programming languages).
It still seems intellectually dishonest to leave it out. Clearly, what Dijkstra in 1972 considers too "clever" may in fact be tools that are now basic building blocks of everyday software. We can all agree that "too clever" is bad. We can't agree on what "too clever" is.
"But, lets discuss the 'for clause' and 'do loop': these constructs were made specifically for one kind of simple loop. It is not a systematic solution for an iterative process."
I'm fairly sure (from what I remember him writing) that he doesn't like it because you can do the same thing with existing constructs, so you'd be adding complexity to the language that isn't strictly necessary. History has shown that actual programmers prefer having for loops like Algol.
"To me it seems Dijkstra specifically aims for languages such as LISP (which, with the renewed interest from Clojure is one of the most oldest successful (semi-) functional programming languages)."
Probably not, or else he would've talked about LISP in a different manner (he does talk about it in an earlier paragraph).
Yes, programmers prefer it, but the general structure:
`for (init-statement ; boolean-continuation-expression ; iteration-statement) statement;` is syntactic sugar for a specific imperative process (with the iteration and ending expression appended at the end of the block). It does not generalize to other imperative processes, it cannot be transformed into a meaningful expression and it invites 'clever' programmers to do 'too much' in the various for-clauses.
Wrt to LISP, do you mean this part? That seems to align well with my standpoint: use very few basic principles and be stable.
"The third project I would not like to leave unmentioned is LISP, a fascinating enterprise of a completely different nature. With a few very basic principles at its foundation, it has shown a remarkable stability. Besides that, LISP has been the carrier for a considerable number of in a sense our most sophisticated computer applications. LISP has jokingly been described as “the most intelligent way to misuse a computer”. I think that description a great compliment because it transmits the full flavour of liberation: it has assisted a number of our most gifted fellow humans in thinking previously impossible thoughts."
Of course we agree. Too clever is "stuff I'm too lazy to understand". What we don't agree is on the definition of I; everyone has their own binding for that symbol, which carries a context for different types of stuff we are too lazy to understand.
If you have to spend time explaining 'what' the code is doing, it is "too clever".
If you have to spend time explaining 'why' the code is doing something.. then "them's the breaks working in a complicated world".
Code should only be as complex as the problem domain.
It's not absolute. It's something to be negotiated at a particular shop.
-- Alan Kay, The Computer Revolution hasn't happend yet — 1997 OOPSLA Keynote
Alan seems to make an overly broad statement, which is funny, but also weak.
Dijkstra decided to program using pencil and paper much later, and mostly because he (like most good programmers) already knew the solution before writing it down.
I correct you again for implying he wrote purely academic code. This is plainly false: for example, he specified part of and implemented the compiler of the Algol-77 language. He implemented one of the first multi-layer (ring-based) OSes.
And then you mention the day-to-day programmer is not in need of academic code. To the contrary! Dijkstra argued that the software crisis (the gap between what computers can do, and what they are actually doing) is caused because day-to-day programmers do not have the right tools and knowledge at hand. In EWD 340, Dijkstra argues this is caused by the mental overhead caused by clever tricks and languages allowing these tricks.
As a professional, I am constantly relating to concepts and code which have an academic basis. Examples are from type theory, lambda calculus, paxos, map-reduce, queueing theory, compression algorithms and many more. I have seen many programming languages, wrote assemblers, interpreters and compilers, worked professionally with imperative, OOP, functional and (higher-order) logical languages, so I dare to say that programming is not about what you can do with the language, but what the language does with you.
I know I'm preaching to the choir here. I know we've all come back to our own code six months later and had to puzzle it out.
More on cleverness from an old AI textbook (Artificial Intelligence Programming by Charniak, Riesbeck and McDermott):
"1.12.5 Cleverness
Avoid it . Clever tricks ... [Lisp specific stuff omitted.]
To paraphrase Samuel Johnson, we advise you to look over your code very carefully, and whenever you find a part that you think is particularly fine, strike it out."
I think the more esoteric features should be reserved for complex code, especially if it's possible that those features can prove runtime correctness at compile time and result in easily-composable modules. I wouldn't expect more than one or two developers to need to develop such a subsystem, however, so the "clever" is used in isolation.
> No map, no flatMap, no fold, no generics, no inheritance… Do we miss them? Perhaps we did, for about two weeks.
So they weren't complaining about map and flatMap, they just missed them.
Good for them! Their problem domain is likely so simple and neat that it fits the Go's limited built-in types well, and does not lead to frequent copy-paste programming. If so, Scala has been an overkill.
users.filter(_.active).map(_.email)
to be easier to read (and not a pain in the ass to type) than: emails := make([]string, 0)
for _, u := range users {
if u.active {
emails = append(emails, u.email)
}
}
return email
The first style is more explicit about what you want to do, the second style is more explicit about how you want to do it. Go made imperative vogue again, but for me it feels like a throwback all the way to Turbo Pascal. Sure, the Go compiler is really fast, but Turbo Pascal would beat it hands down - on 20Mhz machines with 1MB of RAM.With more features (TP 7)!
I mention this as neither a programmer on either Scala nor Go.
If it is of any relevance, I've been writing code for about 20 years across C, Java, Javascript, PHP and shell scripts. I'm sure other FP developers will roll their eyes to a traditional developer like me but the one-liner you mention is simply not self-explanatory for an outsider when reading it.
It returns 'email' instead of 'emails' which I assume was a typo, which of course the compiler would complain about, are you claiming that a variable name typo is somehow a 'subtle bug' ?
Only other thing I see directly is that 'users' is undefined, but this is a code snippet after all, not a working program.
Those dumb Go programmers and their toy programs (https://quicknotes.io/n/1XB0).
However, now that those features are part of the majority of mainstream languages, I surely don't want to go back to how I used to write software in the first two decades of my career.
Being "a better C" was the original battle cry of Go. It's definitely better than C (well, at least for some thing), but it doesn't mean all of these complex C software people used to write are toys.
Go is obviously not a useless toy language either. But that doesn't mean you won't miss features from more expressive languages. The amount of calls for generics from Go users tell a different story.
I think it is from Non-Go users who would use Go if only it had generics.
They guys from Movio did; their problem domain is likely different.
There are cases when the restrictions of map() and reduce() are necessary, most famously for a map-reduce in distributed programming, but that's not really relevant for implementing a simple function.
Also, error density is relatively constant per line of code according to most studies (this is a very general statement, but we can assume that most code just isn't trying to be clever). I've forgotten to add(), append() and drop items inside a loop far more than I've made mistakes inside more functional methods.
And, man, tail recursion in a language with function head pattern matching (ML family, Erlang) is so much easier to read than any complicated for loop.
(Update: realized afterwards that "foreach" isn't quite the same as "for", but the larger point still stands, mostly.)
They describe the intent of what to do with the data, not how it is done.
Just like using SQL versus an old xBase database to access data records.
We all know real programmers used gotos.
The very reason those other functional constructs were split out was not due to need, but due to -clarity-. And that same clarity applies to why to use one of those constructs instead of a for-each loop.
def filter_broken(widgets):
broken_widgets = []
for widget in widgets:
if widget.is_broken():
broken_widgets.append(widget)
return broken_widgets
The actual errors with loops and mutable objects come from complex nested loops, continues/breaks (multi-level ones especially!), extra boolean conditions, and sharing mutable objects between parts of the code base.I think that overstating the supposed risks of for-loops doesn't help anything. The problem is not that you're going to mess up a simple for loop. It's the ways that your code doesn't compose well that are more problematic. Oh, and the verbosity sucks too.
Maybe that's right, maybe that's wrong, but it's indeterminate from the code whether it's desired, whereas a filter function and a reverse function would make it explicit.
Btw: I'd venture to guess that you're not a Python person, and that's why it's not obvious that it constructs the method in the same order. In imperative languages (where Python isn't the perfect example b/c it has filter and list comprehensions), you get so used to these loops that you don't have to worry about questions like that.
If you wanted a reversed list, you'd either name your function appropriately, or call reverse after you run the filter method. It's a little painful to do the first bit.
Because having three specialized functions for three specialized operations makes it easier to see what a given piece of code is doing. One function per function.
I dabble in what sorts of programming languages, but when comes to work, it is mostly C#, Java, JavaScript and some occasional C++, because that is what customers pay for.
Hence why I see a big value in mainstream languages slowly evolving into multi-paradigm ones, as many of us don't have the luxury to move beyond the first class languages of each platform.
If your brain is organized enough to write clean for loops then ... maybe. But it's a bit opportunity for problems. At least to me; but then I may not be smart enough.
That being said, map and flatmap force immutability in some way. So you pay a price for this, either in speed or in memory, even with tail recursion.
And honestly, sometimes I don't know if code is too "clever" or the reader is just too "dumb".
IMO, the answer is people vastly underestimate the cognitive cost of reading code. "Code" rarely exists in a vacuum, the reader has a universe of inputs, outputs, problems, solutions, failures and goals. The cognitive cost of trying to read someone's code is something that must be paid multiple times per day and at one point do you have to wonder - is the gain in expressiveness for a single programmer worth the cognitive cost of the multiple programmers who have to parse that code.
I'd imagine the answer is no - especially for companies that spend more time iterating and patching based on customer feedback than actually designing and architecting systems. My belief is that expressive languages have their place but companies are far more likely to build their software in a "patch-test-iterate" environment which favors less expressive languages.
Not everyone feels this way, the investment to learn these features may not pay off right away, and if you come from a "move fast and break things" language (python/php/js) it can feel like a waste of time.
Issue is that a beginner pre-conditioned long enough on "verboser, imperativer" models/languages, when reading/comprehending such a codebase, has to literally mentally expand every 1 line into 10 upon reading, for quite a while .. some weeks, some months, as more intuitive grasp sets in slowly over time. Probably what happened at OP's quoted company and with the different speeds in comprehending what "clever" (compact) code some of their coders came up with, what they flippantly called "code that was harder to understand by others"..
Language is important and its expressivness. Babel 17 is must read.
There are two problems with this argument. First, brilliance in code is not a property of what linguistic abstractions the code makes use of, but the elegance of the algorithm and engineering. I sincerely doubt that no brilliant code had been written prior to the introduction of your favorite linguistic abstractions. Second, even supposing you're right, what difference does that make? If your code's maintainers are dumb, do you think that you could change this reality by making life harder for them, or is it your job to adapt yourself to the reality of the system you're a part of? When creating something for dumb people to use, is it a sign of good craftsmanship to make it harder for them to work with?
e.g. https://code.facebook.com/posts/302060973291128/open-sourcin...
This is not an absolute, and needs to be answered in the context of the question: "Does my team work?"
If you ever wonder this, then it's definitely a problem with your code and not the reader. Code that cannot be easily read and understood by others is worthless in any commercial setting.
If, for instance, someone were writing C# and insisted on hand-rolling for loops in all cases[1], because LINQ is "too hard", that's them being lazy and unwilling to invest a tiny amount of effort in learning more effective methods.
[1] There are some cases where it's more performant to use a plain foreach or bare for, but unless you're in tight loops or dealing with ginormous collections, it's a premature optimization.
For instance, python has never had the problems that ruby or perl had.
I never said that. I'm only asserting that no language is completely immune, because hubristic cleverness is intermediate to stupidity and genius.
I think that there are cultural norms and expectations that come with certain languages that make them more or less susceptible to "cleverness."
Cultural norms can indeed help or hurt.
"Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?"
Clever code for the sake of being clever is rarely worthy it.
That said, I do tend to have that mantra a little more present in my head when I'm working with Python.
But given that discipline, scala would outshine go since scala has much better typechecking than go - e.g. http://getquill.io/ can typecheck against a running DB schema, etc.
They don't understand that unclear code is probably the number one cause of technical debt. Nobody wants to waste time trying to understand it so they start to attach workarounds and it just keeps getting worse.
Some of their code is so "clever" that I've refactored the line with 7 method calls just to understand what the hell is going on.
Lambdas and fluent syntax make me quiver with fear. In the wrong hands they let you do unspeakable things
> They don't understand that unclear code is probably the number one cause of technical debt.
At the same time, verbosity can have an obfuscation quality all of its own. For simple assignment, I find a ternary operator very clear and concise, and much preferable to a 5-9 line (depending on style) if/else for a simple assignment. It also might keep you from using the single statement version of if/else if your language supports it, and that's probably justification in itself given how many problems that's caused in the past.
Specifically, I think:
usefulMetric = wantComplexCalc ? complexCalc(foo)
: simpleCalc(foo);
is preferable to: if ( wantComplexCalc ) {
usefulMetric = complexCalc(foo)
} else {
usefulMetric = simpleCalc(foo)
}
even if only because it doesn't obscure intent with what is essentially boilerplate. val usefulMetric = if (wantComplexCalc) {
complexCalc(foo)
} else {
simpleCalc(foo)
} useful_metric = complex_calc(foo) if want_complex_calc else simple_calc(foo) die "Invalid param: please enter a positive number" unless $param1 > 0;
$param2 = 0 unless defined $param2;
return undef if $param1 and not $param2;
my $foo = 1 if $bar; # This unfortunately creates a closure around $foo and is a big source of bugs.
As much flak as Perl gets, quite a lot of thought went into making it flow similar to how people think and talk (which is no surprise if you know Larry Wall is a linguist by training). There were some missteps, but it was very early in this area, so that's expected.Yet there have been modules that support it for years, and with much more features than what was recently rolled out (which was meant to be conservative).
Here's[1] what I said about this quite a while ago. Named parameters with type checking (unfortunately at runtime). I've been writing Perl using different modules (Function Parameters) which use the same syntax for about six years now (for functions, not all the sugar on Moose objects).
> Flattened lists are another one
Flattened lists never cause me a problem. If they cause someone problems, I think they've never really learned what context is in Perl. Once you know how context works in Perl and had a chance to use it to good effect, I can't imagine this complaint persisting. Perl is fundamentally different than most languages in this respect, even if it looks superficially similar to more procedural languages. This is actually a cause of a lot of problems for novice users, because they assume their experience in C/Algol derivatives will map exactly, and where it doesn't people get frustrated.
> but the worst is having to specify "use 5.020;" if I'm using Perl 5.20
What? You don't have to do that. If you want to use newer features that utilize keywords which may conflict with whatever you've written or whatever modules you are using, then yet, you need to opt into those. Perhaps you would have preferred if it silently just broke?
> They've even carried this "tradition" into Perl6 where you have to specify "use v6;" at the top of EVERY damned script.
No, you don't. If you do, and you run it in Perl 5, it will automatically swap out the interpreter for whatever Perl 6 interpreter you have in $PATH though.
> Prefixing every variable with "my" is another one which found its way into Perl6. Why can't an advanced language have default lexical scope?
The requirement to define your variables is not because it's not lexical by default (it is lexical by default, you can use no strict to see). .It's strictness which is enforced, which has been found by the Perl community to be vastly preferably to automatic instantiation of variables because it prevents bugs, and prevents a lot of confusion. You have to define your variable, because the Perl community found that a more sane default.
usefulMetric = wantComplexCalc ?
(complexity > 40 ?
superComplexCalc(foo) :
regularComplexCalc(foo)) :
simpleCalc(foo);I believe part of learning a new library/framework/language is to limit yourself to a certain subset of the API offered. After working with Ruby (the language) and Javascript (the ecosystem), I feel like that's the only way to preserve your sanity and productivity. I don't need to know 4 different ways of creating a lambda in Ruby, selecting 1 that can express the other 4 is good enough.
---
In this case, the rule would be no ternary operators, since they work well unless you nest them or unless you make them long/complicated.
Other examples -
You don't need to wrap if conditions unless you have a multi-line body:
if (myCondition)
x = 42;
y = 23;
Early returns simplify short circuiting logic unless your function becomes too long: if (myVariableAtBeginningOfFunction) {
return true;
}
...
// 2 screens later
...
if (x == 42) {
return false; // why am I not getting false?!
}
Using a variable as a conditional in javascript to test against undefined works well unless the value can be falsy: if (person.isStudent) {
showSchool();
}
if (person.age) {
showBirthCertificate(); // what if age is 0?
}Why not "no nested ternary operators"?
> You don't need to wrap if conditions unless you have a multi-line body
Why not "keep unwrapped if conditions on a single line"?
> Early returns simplify short circuiting logic unless your function becomes too long
Why not "keep functions short"?
> Using a variable as a conditional in javascript to test against undefined works well unless the value can be falsy
Why not "only use conditionals on boolean values"?
I'm not saying your rules are right or wrong, I actually follow a couple of them myself, but your wording implies that other people are simply not following rules, or their rules have a lot of nuances and special cases, but the reality is more likely that their rules are different.
Ultimately we all make different connections and form different patterns in our head. As long as a team can agree on a code style, within a few months everyone starts developing the same cognitive patterns.
If you come into a 3 year old codebase and during the first 2 weeks you need to add extra functionality to a 30-line function with an early return, are you going to refactor the early return? Or are you going to extend it into a 32-line function? What about the new hire after you?
Alternatively, your team has decided to embrace the "only use conditionals on boolean values" philosophy. You're working with a section of code that reads `if (myVar)`. It's been 3 hours, and you don't understand why the code's not working. Suddenly, you realize that at some point `myVar` was refactored from a non-nullable boolean to a nullable number, and someone missed changing this.
And the biggest offender yet - code that is grouped within a file into 'logical sections'. I've never seen this work out. What is a logical grouping for you is a confusing pairing for me. Or maybe it's that I can't immediately grok all 2000 lines of a file I've never seen before, and know where to place the method. This madness around code location is one of the quickest ways to code rot.
---
The perplexing thing to me is that these situations are completely preventable.
If you don't use early returns, scenario 1 won't happen.
Scenario 2 won't happen if you use real comparisons e.g. `if (person.age !== undefined)` (Similarly, `if (person.age != null)` breaks when null and undefined start meaning different things...)
And lastly, a canonical alphabetical/visibility ordering for methods in a file of any length is unambiguous. I don't care what the order is, as long as there is a canonical order.
---
I understand that other teams have their own rules. It's no trouble at all to adjust to things that are purely syntactic differences. But when the rules that are chosen hide lurking semantic pitfalls...I don't know why you'd risk shooting yourself in the foot.
A lot of my strong feelings on code style come from the book Code Complete. I highly recommend that to everyone who hasn't read it. It's filled with examples of confusing/broken code you might inherit, and teaches you how to avoid creating it yourself.
Edit: looks like we hit the HN thread depth limit. Happy to continue this over Twitter, check my profile.
usefulMetric = wantComplexCalc == false ? simpleCalc(foo)
: complexity <= 40 ? regularComplexCalc(foo)
: /* else */ superComplexCacl(foo)
;I'm not saying that one should go crazy with one-liners or uncommon patterns, but things like ternary operators used with reasonably short expressions in a single line of code are totally valid and should be readable to any average dev out there.
Particularly
> Also keep in mind that you're probably bothering others, more skilful ones, with unnecessarily verbose code which is to them harder to quickly scan through.
I stopped contributing to one Powershell repository because author thought that ps is hard and he wanted Get-Process. I put a "i am the greates babysiter meme" in PR and that was considered very disrespectful
Particulary
> Also keep in mind that you're probably bothering others, more skilful ones, with unnecessarily verbose code which is to them harder to quickly scan through.
Not sure I can think of too many situations where it would be otherwise.
Here is the meme:
http://content.randomenthusiasm.com/d4ZEVg4VB.jpg
Not exactly profane I would say but what do I know ...
Sounds like it was disrespectful too.
Terse code isn't much faster to read, the difference between a 300 and 400 line file isn't significant.
Your attitude of "he isn't 1337 enough to understand my code" is the logic that leads to no comments and horrible to maintain code in the first place
It doesn't take a lot longer to sit down and understand how ternary operators work, either. C'mon, it's not differential equations, it's just a notation, and a fairly simple one. It might take a newbie slightly more time at first to understand the logic. We've all been there once, you stop and stare at it for 15 minutes, but after a few times of deciphering it you get used to it. It's not about being 1337 (I surely hope that it's not what's considered elite this days), it's about learning new stuff and any averagely intelligent person can do it. Honestly, would you really hire someone who is not capable to (in a reasonable amount of time) teach him/herself how to read a ternary operator? What programming would that person be capable of doing in future?
Smart people can disagree, undoubtedly, but there's a reason why GOTO is considered to be brain cancer and pattern matching is generally considered to be great.
On a lot of those I know what makes code easier for me to read. It's not the same as some of my coworkers. Based on some of your phrasing I suspect we'd agree on a lot of them, fwiw
When the metric used is "easier to read" it becomes far too subjective IMO, things you mentioned are similar but not quite the same
I don't really see highly competent devs doing the whole J2EE architecture astronautics anymore, nor using single-char variable names. There's a tendency to write things concisely when simple, and then moving the complexity away to some other place, stashed in its own function, when it reaches a certain mental threshold of complexity. There's a tendency to use the best features languages have to offer, maximizing simplicity through orthogonal features and repeated idioms, while discarding unnecessary cruft; one of the marks of junior devs is their desire to try to fit problems into new idioms just to test out language features or strange design patterns.
Given that human intelligence is fluid but the variance just isn't that high (after all, we all have a similar amount of working memory), common design practices emerge out of this understanding for our limits for reasoning about problems. Exceptions abound in extremely technical and complicated problems (just check out non-trivial linear algebra code or bit-flipping, low-level device drivers), but the most part it just sticks out how things are made to look simple within a finite range of tradeoffs. This has been my experience in my domain of expertise; look at most current web development frameworks and they respect common patterns even in radically different languages that are really about the essence of the request-response cycle, not made-up constructs of additional complexity or a restating of the problem.
Yes we do, because the customers and their in-house architects decide how it is going to be, not us.
My pet hate.
If the 'idea' of the code isn't easily broken down into abstractions, even mentally speaking, then small functions will just obscure what's actually going on by pointing out all the implementation details that are wound together.
Look at any large C codebase and you will see plenty of goto statements to manage resource cleanup. The problem is using goto in place of structured control flow like loops and if/else. Statements like yours make it seem like there is a conceptual problem with a jump.
But all the examples of this that I've seen are still structured it's just that the language isn't able to express that structure. The most common examples are jumping out of nested loops (better solved by allowing named loops so you can use break/continue) or jumping immediately to error handling logic (better solved by exceptions, or even just simplistic try/throw/catch).
I do think there's a conceptual problem with arbitrary jumps.
https://www.visualstudio.com/en-us/articles/devopsmsft/code-...
Peter Naur - Programming As Theory Building http://pages.cs.wisc.edu/~remzi/Naur.pdf
Scala provides a whole new level of ability for people to write code that can be meaningless to others.
It really is quite different.
+ Any decent developer can read decent code in Java or whatever normal language and get along just fine.
+ Only a few people can deal with Scala - and even fewer if there's a log of specific project Scala weirdness used in a particular program.
So sure - among a narrower set of 'Scala friendly' developers, and possibly within that even narrower set of people familiar with the 'Scala weirdness' of a particular project - those people can 'get along fine'.
The problem is that this can be a pretty narrow set of people.
Scala would have to represent a pretty big advantage to propose it's general weirdness as something to bother with.
I don't think it does - hence the 'de-adoption' of various entities.
My gut tells me it's past the threshold - the 'extra power' offered Scala just isn't quite worth it's weirdness for most things, and so most devs won't learn it ... and so then it becomes less valuable from a business perspective.
It's possible we may have it peak Scala.
We'll see I guess.
True, but once get into the length of variable names in iOS and Android development, you're in a whole new territory. 38 character variables have no place in life.
And extreme's like:
outputImageProviderFromBufferWithPixelFormat:pixelsWide:pixelsHigh:baseAddress:bytesPerRow:releaseCallback:releaseContext:colorSpace:shouldColorMatch
This is 149 character.
This is what happens in almost every language. Niftyness and the prospect of impressing your coworkers distorts the cost-benefit calculation. This is in addition to the true costs appearing months or years after the code is written, involving the interaction of complex factors, like increased cost of debugging.
"Clever" should be regarded as a limited resource. Also, the shop should encourage a culture where "clever" with regards to making code easier to understand should be valued above all else.