8 ms·
Yeah but Rob Pike's idea of simplicity is him personally not having to implement things. If every one else in the world has to implement the same thing a thousa
by Avshalom 9y ago
Yeah but Rob Pike's idea of simplicity is him personally not having to implement things. If every one else in the world has to implement the same thing a thousand times a day he still thinks his thing is simple.
- stcredzero 9y agoIf every one else in the world has to implement the same thing a thousand times a day he still thinks his thing is simple. Smalltalk all over again!
- pjmlp 9y agoActually even Smalltalk is more feature rich than Go.
- stcredzero 9y agoThe community had a real "Not Invented Here" problem in our earlier years, which we never really overcame.
- pjmlp 9y agoI see, as I only used Smalltalk during university for project assignments (Digitalk Smalltalk/V), I never got to experience that.
- stouset 9y agoI wish I could upvote this comment a dozen times. Much of go’s “simplicity” is a Faustian bargain that comes at the cost of unnecessary complexity in each and every project that winds up being written with it.
- tapirl 9y agoThis is called trade-off. The reality world is never perfect.
- stouset 9y agoOkay, but a trade-off that forces complexity into hundreds of thousands of programs to avoid complexity in one is poorly-conceived.
- ghughes 9y agoCare to illustrate your point with an example? I’m wondering what kind of “unnecessary complexity” you’re talking about.
- stouset 9y agoI mean, generics are kind of go's whipping boy. Lacking generics means you end up with copy/pasted code for utility functions that should be part of the go stdlib in virtually every project. It also means copy/pasted code for any data structure you might want to use that's fancier than an array. Go's "simplicity" of error handling (read: lack of any actual error handling abstractions) means you don't get useful things like stack traces and have to manually grep through code for nested error messages. It also makes go code difficult to read at a glance, since virtually every statement winds up wrapped in repetitive error-handling code that doubles or even triples the amount of code in the happy path. The error-handling pattern of using tuples, but no syntactical ability to operate on data within a tuple means you almost never have the ability to chain function calls like `a.b().c().d()`. Instead you have to manually unwrap the value and error, return if there's an error, call the next function, manually unwrap the value and error, ad nauseam. The "idiom" of gift-wrapping error messages is absurd — you are replacing machine-based exception handlers with expensive, slow, error-prone, and less-capable meat-based exception handlers. Having a half-baked type system means you end up having to frequently write type-switches which are checked at runtime to do any sort of generic code. There's no functionality in the language to ensure that all possible options for that type switch are exhausted, so you are virtually guaranteed to get runtime bugs when a new type gets written and later is passed in. Speaking of type switches, they interact poorly with go's indefensible decision to have interfaces implemented implicitly rather than explicitly. I have seen types get matched to the wrong typeswitch in producion code because a new method implemented on one type caused it to accidentally "implement" an interface used elsewhere in a typeswitch. Good luck ever catching this before it hits you in production. Go's concurrency primitives are useful, but the lack of ability to abstract over them means that you have "advanced go concurrency patterns" dozens of lines long and involving multiple synchronization primitives for what amounts to `a | b | c` (https://gist.github.com/kachayev/21e7fe149bc5ae0bd878 https://gist.github.com/kachayev/21e7fe149bc5ae0bd878). God help you if you want to implement something like parallel map. God help you if you want to implement something like parallel map for n > 1 types. Go requires you to manually remember to release resources you've acquired with `defer`, instead of sanely having There is no capacity in the language to enforce that you've done so, and it is virtually impossible to find e.g., a missing `defer fd.Close()` in a large code base. God help you if you leak file descriptors and need to track down the source. Go's inability to perform any meaningful abstractions also means that you have to know all the details of code you import. It's difficult to make code a black box. Case in point: to do something as painfully simple as reading a file, you need to import bufio, io, io/util, and os. During the course of writing this post, I forgot more examples than I listed — I literally could not remember them all in my head as I was writing them down. This isn't simplicity, this is utter madness.
- ademarre 9y agoThe cost of complexity in a programming language is paid not only by the developers of the language but also by the programmers that use it.
- stouset 9y agoThe cost of poorly designed complexity maybe. This is the same mentality as people who throw up their hands and say government is broken, so we should deprive it of resources to make it as small as possible. Doing this just winds up makes the problem worse, when there’s plenty of evidence that well-funded governments can work well. It’s also the same broken mentality behind schemaless databases. Schemas are hard, so let’s get rid of them. This backfires because you haven’t actually rid yourself of schemas, they’re just implicit and now you lack any tools to operate on them meaningfully. “Hard problems are hard, so let’s just avoid dealing with them” is not a sustainable solution in the long term. Sometimes they’re really hard and ignoring them makes it worse. Sometimes they’re only hard because we haven’t thought about them in the right context. And sometimes hard problems can be sidestepped entirely with a bit of cleverness. But outright ignoring them and hoping they go away just punts the hard problems to others.
- omginternets 9y agoI think this is confusing simple (as in simplex) with facile. Simplicity implies a small set of features; it's a property of design. Facility implies ease of use; it's a property of operation. Go unambiguously favors simplicity, at the expense of facility. This is why we're given raw CSP primitives (sharp edges included, e.g.: goroutine leaks) instead of a full-fledged actor model.
- tapirl 9y agoWhat are the differences between raw CSP primitives and ull-fledged actor model?
- jitl 9y agoIn an Actor model, you can explicitly send messages to actors (send), monitor their state (executing, failed, completed), and take actions against them (kill, spawn). A fallout from this is that you can have one actor supervise another, to protect your program from errors: https://hexdocs.pm/elixir/Supervisor.html https://hexdocs.pm/elixir/Supervisor.html The Golang runtime provides only these actions: (spawn). It’s up to the author to create communication channels, and it’s impossible to query for the status of a Goroutine. This means you have no explicit control over goroutines, so you can’t kill misbehaving ones like you could in another language.
- tapirl 9y agoI feel raw channels provide more possibilities than Actor model. In fact, you can build such an Actor model by warpping CSP channels. It is not too hard.
- omginternets 9y agoYes, I think this is precisely the idea. Anecdotally, 99% of the complaints I read WRT Go can be solved by using libraries that already exist. If one wants a batteries-included-in-the-stdlib form of facility, one should look at languages like Python.