I strongly disagree with this notion of "simplicity" as being attributable to scarcity of language features. Some of the languages that I felt were the easiest to use had quite a number of language features, but had simple <i>semantics</i>. I think Rich Hickey nailed this in his "Simple Made Easy"[1] talk. Complexity is <i>not</i> about additivity, it's about entanglement.<p>[1] <a href="http://www.infoq.com/presentations/Simple-Made-Easy" rel="nofollow">http://www.infoq.com/presentations/Simple-Made-Easy</a>
From reading the abstract, it sounds like this addresses precisely the topic of "Worse is Better" [1].<p>There are frequent misunderstandings of this essay -- the argument isn't as coarse as "crappy software wins", or "release early and often".<p>The tradeoff is: do you want a simple interface (MIT style) or a simple implementation (NJ style)? If you want a simple interface, you have to hide a bunch of complexity underneath. If you want a simple implementation, you punt on some hard things and expose it to the user.<p>Despite its authorship, and marketing as a better C (which is the epitome of NJ style), Go is MIT style! The concurrency model hides a lot of stuff under the covers: sync vs async syscalls, segmented stacks, etc. And GC is probably the ultimate MIT-style feature, in that the interface is very simple (just pretend you have infinite memory), but implementations are tremendously complex -- basically each GC is unique and its own research project.<p>[1] <a href="https://www.jwz.org/doc/worse-is-better.html" rel="nofollow">https://www.jwz.org/doc/worse-is-better.html</a><p>EDIT: From paging through the slides, this seems to be basically the gist of the last half, but I don't see that he mentioned "Worse is Better" or MIT vs NJ style...
I think that code readability is different from system comprehensibility, and that readability is merely a subfactor to comprehensibility. Readability often means the time it takes for me to grok a function, module, or unit of code, and often what fits on my eyeball.<p>System comprehensibility is how long it takes for me to learn the minimal set of atoms in order for me to comprehend an entire system.<p>Rob Pike criticizes map and reduce and other functional idioms as not machine simple. And although Rob Pike doesn't say this, I might argue on his behalf that functional code has a mild reputation for less readability.<p>But I would argue that functional idioms permit tasteful tradeoffs between machine simplicity and system comprehensibility. I would also argue that when a system gets to a certain largeness, I would be sometimes okay with sacrificing some readability and machine simplicity for system comprehensibility. I would also say that although I might lose some performance advantages with a functional approach, the improvement to system comprehension might mean an improved cognitive capacity for the human to optimize for distributed multi-core contexts, which might offset the losses from machine simplicity.
This talk has a lot of very weak points. He made the claim that more features hurt readability (~6:10), because when you are reading you have to waste time thinking about why the programmer chose the set of features he did to write the code. To make that kind of claim without qualifications is just ridiculous - if it were true, why add any feature to a programming language at all? Especially if one believes readability is the most important thing in a programming language, which Rob Pike says he does. It may be true that <i>some</i> features hurt readability, but it is obviously not the case that all features hurt readability for all people all of the time.<p>It is also strange that he makes so many claims about readability, considering it is the most subjective attribute of a programming language there is. For example, some code that would have been completely unapproachable to me a couple of years ago is perfectly readable to me today, because I've learned new concepts.
I'm trying to think how the simple/complex from this talk compare to the simple/complex from "Simple Made Easy".<p>The part about how features should be orthogonal does seem similar to the "not entangled" (and to DRY / SRP).<p>The part about visible surface area -- garbage collection is "simple" because you can't explicitly talk to it -- seems to be a closer match for "easy" tho, as do the examples (smell test: anything with "and" in the name probably means there are entangled concepts).<p>...huh. Which means that his concept of "simple" <i>does</i> in fact contain multiple things entangled together, or in other words is complicated. ;-)
I can't even begin to measure the amount of time I've wasted trying to make go code look as concise as what I can have trivially in C where things like macros and do { } while() are available.<p>Instead of "simplicity" saving me time as Mr. Pike suggests, it leaves me dissatisfied with the results after wasting my time in attempts to prevent my dissatisfaction.<p>This probably isn't an issue for someone new to programming without the expectations experience with other languages may bring, but for a seasoned C programmer Go often feels like a regression.<p>IMHO, what largely makes Go useful over C is the language-level support of coroutines, and not because the simplicity of the "go" keyword or channels, but because it eliminates the need to design and integrate with snowflake event loops or the specifics of blocking/non-blocking file descriptors.<p>As a result, packages are written without concern for how the caller's event loop will be integrated, you just write blocking code everywhere and it can be used by every Go program with significantly less potential for impedance mismatch. The garbage collection and overall elimination of the need for allocating and freeing memory also contributes to this; no longer do you have to understand the resource life-cycle strategy of some C library you've decided to use, or if it's using the C library's allocator or its own, or if it's capable of using your allocator via some elaborate and unique initialization scheme.<p>Unfortunately Golang's concurrent execution model is also what makes it a pretty terrible language for system programming. Golang's inescapable and largely opaque underlying use of threads is a constant source of pain for anyone programming operating system facilities which influence only the calling thread's state, particularly state inherited by child threads.<p>I often encounter misuse of runtime.LockOSThread() in attempts to overcome this problem but as of now this is completely inadequate because the Go scheduler creates new OS threads on an as-needed basis from whichever currently-executing OS thread happens to be executing at the time. This leaks whatever unique state the thread may have into a newly created, unlocked OS thread, which may then go execute any goroutine - likely the very thing you were attempting to prevent in using runtime.LockOSThread().<p>There's plenty more to whine about, but I'll stop here.
I absolutely agree with all Rob Pike says. But in practice the problem is still the trade off. How limited a language should be ? what are the features a language should absolutely have to make the life of the developer easy yet still give him some room to express himself the way he wants? I think that Go failed if one considers that trade off. Often with Go, a developer ends up doing the job of the compiler, because its flat type system doesn't allow a certain degree of expressiveness ( or worse, forces the developer to use reflection , i.e. putting aside the type system all together and using a "generic" 'interface {}' type).<p>While C++ is something extreme in term of complexity , the other extreme, Go, is problematic as well. I've yet to find a language that has a good balance between the 2. But Go certainly showed that the tooling is as important as the language itself, and it's a great part of its success.<p>"Philosophically" , no question, I agree with Rob Pike. On practice, things are a bit more nuanced.
I love the vector space analogy.<p>I have been thinking about the same thing: that the simplest languages have a minimal set of "orthogonal" features; though it never occurred to me to describe them in those terms.<p>But hey, it's Rob Pike. I was bound to walk away with something after listening to him talk :)
The <i>mere presence</i> of choice incurs a tangible mental cost, <i>even if that choice is not exercised</i>.<p>In the case of programming languages, this is not only a development cost, it's a maintenance cost as well. But this concept seems to apply to many more things than just programming.
For another interesting talk on "extracting simplicity" vs. "mastering complexity", listen to Scott Shenker motivate Software Defined Networking:<p><a href="https://youtu.be/WVs7Pc99S7w?t=375" rel="nofollow">https://youtu.be/WVs7Pc99S7w?t=375</a>
Some quibbling here in the comments about whether Pike uses the right definition of "simple". There is of course a context to Pike's notion of the word:<p>> The key point here is our programmers are Googlers [...] They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.<p><a href="http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/From-Parallel-to-Concurrent" rel="nofollow">http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Fro...</a>
I think most of his talk is wrong.<p>Or at least wrong when we talk about general programming.<p>Go isn't a 'bad' language. Go is simple another tool for one job and it's doing his job good, cause of his simplicity.<p>However not every problem is so easy to solve with this simplicity. Some problems doesn't fit well with the Go approach. And I always hate it when people talk about "why x is superior because less/more features", mostly things emerge when the problem emerges.
I agree with him in principle although I agree with a lot of the other counterpoints made here too.<p>The biggest reason that other features get added to languages to make them ubiquitous is because in MANY cases companies marry their infrastructure to a particular language. With that infrastructure commitment you end up with an investment into doing everything with that particular language so if you have a feature or methodology in another language that would be good for a particular use case, your existing investments prevent you from being able to EASILY introduce a new language just for that feature.<p>Microservice architecture goes a lot way toward countering those investments. Heroku actually does a great job of removing language specific infrastructure investments from your process that make it easier to introduce language-per-purpose approaches.<p>Wrote a bit article on it for Codeship a couple of months ago. <a href="http://blog.codeship.com/exploring-microservices-architecture-on-heroku/" rel="nofollow">http://blog.codeship.com/exploring-microservices-architectur...</a>
<a href="https://youtu.be/rFejpH_tAHM?t=23s" rel="nofollow">https://youtu.be/rFejpH_tAHM?t=23s</a><p>haha, that's a good one :)