Trying to define an objective term like "simple" is of no use if you can't map it to something in human perception. Let's say we have some all-knowing oracle that can tell us which algorithm for problem X is the simplest one. If nobody would agree, what is the point? Or, if 51% of programmers would say A is simplest while the other 49% consider algorithm B more simple, should we just go "yay majority" and call it a day? In reality we have no way of knowing which one is objectively most simple, because at least regarding this domain, we as humans are unable to make an objective judgment.
Is the simplest solution always the one with the least lines of code? Least number of classes? Singletons vs static classes? Recursive approach vs. iterative approach? C style linked lists (next-pointer in data type) vs. "proper" list class? In general: Could we formalize this, ie. come up with an algorithm how to determine how simple code is?
Often times code that is easier to understand might be harder to work with, while more elaborated code with indirections and abstractions is harder to grasp (simply because it's more LoC/classes, just more reading), but once you got familiar with the code base, much easier to work with. So when you say objectively simple, do you mean "simple to understand so you can tell what it does", or "simple to understand wrt maintaining, extending and fixing"? And which kind of simple should I go for when writing code?
> Simple is better than complex. Complex is better than complicated.
I've stuck by those two lines of PEP 20 since I learned about them. The object is to write simple code, but it's okay to write complex code if your goal is to avoid complicated (unreadable) code.
That somewhat resonates with the definition that complex (code) is about intertwining - 'complex' is problematic because as things get more and more complex, you can have only a local understanding, and can't possibly hope to grasp all the implications of a code change. This suggests that "complicated" is something that is "somewhat complex, but still manageable with sufficient expertise/domain knowledge". I tend to use "complicated" to denote something that has inherent complexities (due to the nature of the real world); and "complex" to denote something that is artificially complex, due to ... well, lack of thought into making it as simple as it could be. In that sense, I view "complex" as being definitely worse than "complicated"
Complicated to me means that complications have been added where they weren’t needed. A Rube Goldberg machine solves a simple problem that has been complicated.
Complexity is a property of the problem; complication is a property of the solution. Whilst over-complication is bad (e.g. your Rube Goldberg example), so is being simplistic (not enough complications to handle the complexity).
Examples of simplistic solutions can be found in all of those "What programmers don't know about FOO" articles (human names, timezones, distributed systems, etc.)
I haven't thought too much about how such things relate, but my first thought is that TDD seems biased towards oversimplification, since we do 'the simplest thing which makes the tests pass'. If we're testing that Bob's user profile shows the name "Bob", then the simplest thing is to have all profiles hard-coded to say "Bob". To fix this, TDD tells us to add more tests which check different names. That's before we've even begun to consider the hairy details of how names are used in different cultures.
It's a reasonable approach, but it's not "solving" the oversimplification problem, it's just shifting it into the test suite. It's similar to the problem of over-fitting in machine learning models, where we may need millions of input/output examples to narrow-down the intended behaviour.
I think the aggressive refactoring advocated by TDD manages to bias it away from overcomplication.
At some point you need to specify which edge cases you need.
As I said, it doesn't solve the problem of overcomplication/simplification, it just shifts the burden on to the test suite instead of the program.
Complex and complicated are, according to the dictionary, basically synonymous, and they mean something intricate or tricky that is composed of many interconnected parts.
I think the PEP authors were trying to say it's better for a piece of code to be clever and tricky to avoid complicated interactions with other code, if that's your only choice. I think they're trying to suggest complexity at a small scale is better than complexity at a large scale. But I'm just guessing, and I do think it could be better stated with two words that aren't synonymous.
So I agree with the above poster that the PEP makes no sense here.
The common definitions of complex and complicated are that they're synonymous, there is no strong or fundamental distinction between them in the way you're trying to suggest. That's the main problem with the PEP statement. Trying to assert there's a difference is likely to result in miscommunication, this uni course and the PEP authors are both making the same mistake.
adjective 1. composed of elaborately interconnected parts; complex: complicated apparatus for measuring brain functions.
http://www.dictionary.com/browse/complicated
https://en.m.wiktionary.org/wiki/complex
https://en.m.wiktionary.org/wiki/complicated
https://www.merriam-webster.com/dictionary/complex
https://www.merriam-webster.com/dictionary/complicated
https://dictionary.cambridge.org/us/dictionary/english/compl...
https://dictionary.cambridge.org/us/dictionary/english/compl...
And if you talk about code there is also changeability. Which is somehow at odds with understandability. To make code easier to change you must move to abstractions, and more abstractions make a code more difficult to understand—but easier to change.
Yet, it is humans who need to read it. This is the end goal.
If you rewrite the code to satisfy a theoretical objective criteria of simplicity, but end up with something that people reading the code find harder to read, then you have failed.
It's the same when you design a UI.
Humans are complicated beasts. Saying "people find the code harder to read" doesn't help me know what I should do. If I have no objective criteria, then it must be subjective, and then all that "simple" means is that "this is easiest for me right now" - which, from experience, I claim to be a very unlikely path to produce code that multiple people agree to be "simple". (except if the problem was trivial to begin with - but then, who cares?)
"Which is the better painting, Van Gogh's Starry Night or a kindergartener's watercolor of their family?", on the other hand, appears to have a correct answer.
There are multiple competing definitions of "simple" with respect to code and multiple kinds of simplicity that often have to be traded off against one another, and reasonable programmers can and do disagree about which compromises to make and where. Code quality (including, but not limited to legibility) is in many regards a subjective endeavor.
That doesn't mean, though, that all decisions are equally justifiable. Trust your sense of aesthetics. It's usually trying to tell you something important, even if you don't know how to put it into words yet. With subjective endeavors there is no perfect, but there is usually better. Chase that.
In the second, the difference is so big that it's obvious for anyone which is better, but that still doesn't make it useful for getting better at painting. "Paint more like a Van Gogh and less like a kindergarten kid" is not useful advice.
People do have art critics and manuals that attempt to write down exactly what made Van Gogh paintings great. Those writings are useful, and many artists do indeed study them.
> If I have no objective criteria, then it must be subjective, and then all that "simple" means is that "this is easiest for me right now"
Here's a few other (actionble) things that "simple" can mean.
* seems to reliably be easiest for me every time I look at it
* was judged to be better by the three or four people I could get to give me an opinion
* follows a heuristic that a bunch of smart people all seem to agree generally makes things better
* mirrors whatever we did on this project the last time we were in this situation
Even if all it means is "easiest for me right now", that's still probably going to get better results than "the first thing that popped into my head that might work".There is no objective definition of simple. On behalf of the maintenance programmers of the world, please don't take that to mean it's not worth trying anyway.
Oh, I agree with that. What I meant was that "this doesn't mean there can't be objective criteria about what is simple and what is not; or that it's futile to attempt objective definitions of 'this is simple/this is not' ".
"Easiest for me right now", in my experience, only leads to simplicity in trivial cases. Simplicity requires intense thought & explorations, it's never easy (except, again, in trivial circumstances).
At least with an objective criteria anyone who doesn't understand your code has something objective they can get better at in order to understand your code. And this is something that everyone can synchronize on from now and here to infinity and everywhere.
But you can absolutely solve many complex problems without straying outside of the set of simple arrangements or simple configurations of purely simple solutions. It may still be hard to understand. It was probably hard to write, too.
It should not be hard to refactor, if at all possible, when the scope of the problem has predictably changed later on.
My intuition tells me that when I've refactored something to be simple-but-hard it ends up making more maintainable code. There is less state being maintained, less lines of code to maintain, less coupling. Yes it requires a bit more time to understand, but there is also less stuff to understand. Bad code and bad decisions snowball into bad projects
power_on_self_test()
while True:
recover_from_everything_that_has_gone_wrong_so_far()
If the only rule you enforce is making sure every piece of state has a damn good reason for existing life is improved.OK, on re-read this sounds like more like a rant, and less like a philosphy of life. Oh well.
There are often times when I could make the code simpler but it would be longer and vice versa and my decision about which one to pick is purely personal.
And then it goes on to implement the solution using TDD. Very underappreciated book, imho.
That doesn't mean you don't have to learn anything to understand it. Going the easy route usually means dumbing down things and while it can help reading in the moment it definitely doesn't help in the long run as the code evolve over time.
The goal should be code that is easy to reason about, given a reader that is familiar with the popular idioms. I think that for most languages, thoughtfully chosing good names and writing idiomatic code goes a long way to reducing the cognitive load in reasoning about code.
I also find that Python's syntax helps readabilty, and that also decreases cognitive load. Over the holidays I spent some quality time reading up on Rust and starting to climb the learning curve. I'm still at the stage that I find Rust's syntax jarring. It looks like so much ASCII salad. I can't help but think that a little more attention paid to natural syntactic flow would make Rust code easier to reason about for the long haul. Of course, I'm only at the beginning stages of learning Rust idioms -- a fair analaysis can only be done after I have learned them well. But still, it looks like Rust values screen real estate over readabilty, which is a misguided optimization IMHO.
My excitement about Rust is mainly the potential for using Rust for embedded real time -- but I see microcontrollers are still 3rd tier support. It seems like Rust the language is directed at exactly that problem, but Rust's build system and overall ecosystem are not embedded-aware, unless there is plan for xargo to get tier 1 support?
So... this isn't the thread for it but I have to ask... the only Rust reference I've seen to list/hash comprehensions is somebody who did a macro. So... is that the idiomatic way to do a list comprehension in Rust? Or is Rust going to make comprehensions first-class syntax? It seems to me that if it can be done in a macro, it isn't a big leap to make it part of the language.
I'd imagine most people reach for iterators when it comes to list comprehensions. If the crate gets popular enough, there might be a thing, but I don't even know the name of the crate you're talking about, so I don't think it is.
I think a challenge with embedded is that the platform landscape is highly fragmented. I would advocate for picking one target CPU and making it tier 2, let the rest stay at tier 3. Pick one that has a cheap development board to use as a reference platform. Don't even try to test everything in the cpu/platform matrix -- just pick one to be the pilot, and the rest will benefit from at least having that one big snowplow clear the road in front.
My specific suggestion would be an Arm Cortex-M4 with hard float of some flavor. That clears the road ahead for a lot of hardware of general interest. ST has quite a few easily available dev boards at reasonable prices, but that is just what I am most familiar with -- there are certainly others. I am also a big fan of Micropython, and the reference board for that is the Pyboard, which might be a good inexpensive choice also, not to mention a good way to spread the open source and open hardware love. It would be easy and reliable to flash a Pyboard back and forth between Micropython or some Rust-on-bare-metal image, I would personally be a fan (that's only one data point, but it's mine :)
If that were true, I suspect it would look a lot more like ML and a lot less like C.
This. Is. Hard.
It does help when the language is simpler. But as I code, when I find the need to deal with more exceptions, more functions, and more words just to get something to work... I just assume I should do it differently.
Unfortunately, that's most of the time I spend coding.
I like Rich Hickey's stance on this: "simple" is objective (antonym: "complex"), whereas "easy" is subjective (antonym: "hard"). Easy depends on skills, interest, patience, curiosity - but simple does not. Simple is about lack of intertwining of concerns. About writing code that can be understood without "bringing in" the entire codebase into your mind. Like, you can understand a pure function just by looking at it (it's simple). If it modifies or uses a global variable, now you have to look at all the places that "touch" that global variable, in order to understand what your function does; the code is thus much more complex.
With that definition - it's absolutely correct to say that the code should be as simple as possible. And that writing simple code is typically very hard.