11 ms·
Rob Pike: Simplicity Is Complicated [video]
- LaPingvino 11y agoThere are several points where he won't mention the language. Which languages do you think he meant?
- f2f 11y agoCOBOL.
- systems 11y agoPerl6
- catwell 11y agoProbably C++ or Java (Go was written to replace them at Google).
- slowmovintarget 11y agoThe first mention sounded like C++. For verbosity (second mention), I'd guess Java. The third mention sounded like JavaScript where he said something to the effect of unexpected tangling, but those are guesses based on what Google uses and Rob's brief description.
- infogulch 11y agoThe mere presence of choice incurs a tangible mental cost, even if that choice is not exercised. 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.
- Ace17 11y agoChoices are made at the time of writing the code. And complex code has proven to be very easy to write. So the mental cost of all the existing choices must be minimal.
- theGimp 11y agoI love the vector space analogy. 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. But hey, it's Rob Pike. I was bound to walk away with something after listening to him talk :)
- chubot 11y agoFrom reading the abstract, it sounds like this addresses precisely the topic of "Worse is Better" [1]. There are frequent misunderstandings of this essay -- the argument isn't as coarse as "crappy software wins", or "release early and often". 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. 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. [1] https://www.jwz.org/doc/worse-is-better.html https://www.jwz.org/doc/worse-is-better.html 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...
- Peaker 11y agoGo is a simple language, with 1970's features. It is very easy to implement, and leaves a lot of the complexity (null pointers, generics, etc) to the user instead of solving it in the language. This is a classic example of worse-is-better. An MIT-style language would be Rust, which indeed struggled to get to v1.0 and Go vs. Rust plays out quite similarly to what worse-is-better would predict.
- bjwbell 11y agoThere isn't one language to rule them all. An advantage of Go over Rust is easier tooling due to a simpler language, an advantage of Rust is a richer type system. These are in opposition.
- Ygg2 11y agoEh? Rust tooling is excellent? Rust can reuse nearly all C tools like kcov, gdb, perf, valgrind. On top of it, it has a solid set of its own tools. For example it has rustfmt, cargo, rustdoc (having the ability to test your Rust documentation is great). Only thing it truly misses is a IntelliJ IDE for Rust and possibly a Rust oriented coverage system.
- tbrownaw 11y agoI'm trying to think how the simple/complex from this talk compare to the simple/complex from "Simple Made Easy". The part about how features should be orthogonal does seem similar to the "not entangled" (and to DRY / SRP). 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). ...huh. Which means that his concept of "simple" does in fact contain multiple things entangled together, or in other words is complicated. ;-)
- knucklesandwich 11y agoI 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 semantics. I think Rich Hickey nailed this in his "Simple Made Easy"[1] talk. Complexity is not about additivity, it's about entanglement. [1] http://www.infoq.com/presentations/Simple-Made-Easy http://www.infoq.com/presentations/Simple-Made-Easy
- bjwbell 11y agoHow do you have a large set of language features with them not interacting? In Java, serialization and generics interact with practically everything. In C++, RAII interacts with exceptions, which is the point but isn't exactly pleasant.
- knucklesandwich 11y agoGenerics solve an occurrence of too much entanglement. That is, it solves entanglement of an abstract "shape" of computation with a specific set of type definitions. Generics actually allow you to not think about an additional dimension of your program (i.e. the exact types a computation or data type can be used with). Haskell programmers famously point this out with the observation that a generic fmap is safer than one that has knowledge of the concrete types it uses. The type signature of fmap is this: fmap :: Functor f => (a -> b) -> f a -> f b In practice, what this means is that you can be assured that your fmap implementation can only apply the passed function over the value(s) wrapped in the functor, because of the fact that it cannot have visibility into what types it will operate on. In golang, because of a lack of generics, you can write a well-typed fmap function, but it will inherently be coupled with the type of the slice it maps over. It also means the author of such a function has knowledge of all the properties involved in the argument and return type of the function passed, which means the writer of an fmap can do all kinds of things with that data that you have no assurances over.
- catnaroek 11y agoExactly. Parametricity is the killer feature of statically typed functional languages. This why it saddens me when Haskell and OCaml add features that weaken parametricity, like GADTs and type families.
- jnpatel 11y agoFor another interesting talk on "extracting simplicity" vs. "mastering complexity", listen to Scott Shenker motivate Software Defined Networking: https://youtu.be/WVs7Pc99S7w?t=375 https://youtu.be/WVs7Pc99S7w?t=375
- euyyn 11y agoNice point; thanks for the link!
- i_s 11y agoThis 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 some features hurt readability, but it is obviously not the case that all features hurt readability for all people all of the time. 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.
- jshen 11y agoThere is very little, to no, emetic all evidence either way. Take all of the demands for generics and see if you can find you can find any emperical data to back up the demands.
- placeybordeaux 11y agoThat one part was a weak part. If he claimed that having less features helps writeability I would have completely agreed though. It might take more writing to accomplish what you want, but frequently the choices you are making are a lot simpler, so you spend less time on them and more time just writing the code that you need to write.
- omaranto 11y ago"If he claimed that having less features helps writeability I would have completely agreed though." I suspect most people disagree with you and think, for example, that the features in high level languages make it easier to program in those languages than in low level ones.
- bjwbell 11y agoSome history might help. Go was created largely as a reaction to writing server side C++. I have sympathy after writing but mostly reading C++ code bases. I swore never again. I react in horror to the features being added to CUDA and SyCL to make them closer to C++. I still hate C++ with a passion, grepping around in gecko is much harder than servo.
- threatofrain 11y agoI 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. 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. 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. 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.
- pcwalton 11y ago> I would also say that although I might lose some performance advantages with a functional approach, the improvement to system comprehension means better human optimization of distributed multi-core contexts. You don't even need to bring in parallelism for functional approaches to gain in performance. Here's an example (in Rust) of how you'd implement collecting an iterator into a vector using the "machine simple" approach: let mut array = vec![]; for element in some_iterator { array.push(element); } Now for the functional, less "machine simple" approach: let array = some_iterator.collect(); Which is faster? Without knowing the details of the compiler, you might think the former is faster, because there are fewer function calls and less indirection. But this is wrong, as the compiler will trivially inline "collect" for you and eliminate any indirection. In fact, it turns out that the second approach is often significantly faster, because the standard library contains the equivalent of: let mut array = Vec::with_capacity(some_iterator.size_hint()); for element in some_iterator { array.push(element); } That is, it uses the size_hint() method to avoid reallocating during the collection operation, reducing the number of memory allocation and array copy operations to O(1) instead of O(log n). What are the chances that you'd do this important optimization manually? Not high: it adds noise, it's esoteric, and it's easy to forget. But if you use the functional idiom, you benefit from the optimized approach every time. That is the true benefit of abstraction, which is really the key point here. "Machine simple" and "fast" are frequently in opposition, and too often people think that the former implies the latter.
- cronjobber 11y agoSome 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: > 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. http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/From-Parallel-to-Concurrent http://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Fro...
- knucklesandwich 11y agoThat quote is a hilariously denigrating claim to make of your coworkers. Even if it were true (which I don't believe for a second), you've created a language released to the community at large. Your users are not just google engineers. If you want to encourage broader acceptance of your language, you're going to need to make a good faith attempt to listen to their requests.
- emmelaich 11y agoI think that it would be taken in the spirit it was intended. Programming is hard. (even when you take into account https://en.wikipedia.org/wiki/Hofstadter%27s_law https://en.wikipedia.org/wiki/Hofstadter%27s_law) Also, I'm pretty sure the "brilliant" is meant somewhat sardonically.
- knucklesandwich 11y agoIf this is all tongue-in-cheek then what does it really amount to though? A good-natured ribbing of Google employees? I'm in total agreement that programming is hard, but I don't think it's hard because of languages that pack in features, its hard because of languages that don't ensure reasonable semantics or provide you the tools to enforce them. Programming is hard because its inherently powerful... languages that allow you to circumscribe that power are what keeps me sane though, not particularly the ones you can fit on an index card.
- 11y ago
- trymas 11y agohttps://youtu.be/rFejpH_tAHM?t=23s https://youtu.be/rFejpH_tAHM?t=23s haha, that's a good one :)
- merb 11y agoI think most of his talk is wrong. Or at least wrong when we talk about general programming. 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. 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.
- aikah 11y agoI 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). 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. "Philosophically" , no question, I agree with Rob Pike. On practice, things are a bit more nuanced.
- lIlIlIlIIlI 11y agoI 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. 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. 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. 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. 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. 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. 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(). There's plenty more to whine about, but I'll stop here.
- zaphar 11y agoAs someone who reads far more C code than I write. Those macros you seem to be missing infuriate me. They hide far too much from me when I'm trying to understand the code I'm reading. And as far as do and while go, why would you hide part of the state determining when you're loop exits at the end of the loop just to save a few lines at the top. I am going to naturally try to read your code from top to bottom not top to bottom to top. The more you make me skip around the longer it will take me to understand what you are doing. 90% of my job is maintaining code not writing new code. Programmers who write as if that isn't the case just make the long term cost of maintaining the software they write larger.
- brightball 11y agoI agree with him in principle although I agree with a lot of the other counterpoints made here too. 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. 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. Wrote a bit article on it for Codeship a couple of months ago. http://blog.codeship.com/exploring-microservices-architecture-on-heroku/ http://blog.codeship.com/exploring-microservices-architectur...