11 ms·
Golang: channels are not enough
- andrewvc 11y agoThis is why clojure is awesome. It's a nice enough language, but where it really shines is that it gives you pretty much every concurrency tool under the sun. You have the entirety of java.util.Concurrent + all the cool clojure tools like agents,core.async,atoms,transactional memory, etc. Can you shoot yourself in the foot here? Sure. But you also have the right tool for the right job if you know what you're doing.
- coolsunglasses 11y agoI think this holds true for only Haskell and Clojure. You're missing something important everywhere else (especially STM). One nice thing about STM in Haskell is that the type system will help prevent you making mistakes. I've benefited from the types tracking how I'm creating and combining database transactions, which makes it easier to compose them into one big DB transaction from smaller ones.
- Scarbutt 11y agoI have never seen Refs(STM) being used in a Clojure open source project, looks like they are great in theory but in practice they are another story, on other hand, core.async is much more prevalent, I do hear a lot about the use of Haskell's STM thought.
- dack 11y agoYeah I think there is a very real cost to going the STM route when you can have arbitrary side-effects, so it's a lot harder for that tradeoff to be worth it than in haskell. That's not to say there aren't any downsides to a language that prevents uncontrolled side-effects, but being able to make more guarantees is one of it's greatest upsides.
- rapala 11y agoIn your run of the mill webapp you rarely have state outside the db and caches. What little you have can often be modeled with a single agent or atom. So no need for refs. What you do have in webapps is synchronization between concurrent processes. For that the STM primitives don't offer much. The core.async on the other hand is a great fit.
- nickbauman 11y agoParallel concurrency is the Great White Whale of our time. Languages like Python and Ruby give you a toothpick to hunt with. Java gives you a letter opener. Go gives you a decent bowie knife. Clojure gives you something like a proper harpoon. But something eventually has to come along at some point to make it possible to not have to take down Moby Dick in the first place. I don't know what that is but I'd love to see it.
- kasey_junk 11y agoThere is quite simply no argument that Java doesn't provide better complex concurrency support than Go. You can argue that the golang defaults are better, but there is nothing you can do in go that you can't do in Java and quite a bit the other direction that reverts to "program C with FFI".
- skj 11y agoUsing that argument you can assert that all languages are equal in every aspect. So, not a useful argument.
- deleted 11y ago[deleted]
- stcredzero 11y agoBut something eventually has to come along at some point to make it possible to not have to take down Moby Dick in the first place. How about something that was built on the design principles of Erlang, but with much faster execution speeds?
- nickbauman 11y agoSuch as?
- dack 11y agoI used to be a big proponent of Clojure because of the tooling and solid library support (what you mentioned, but also persistent immutable data structures). However, I never got over the runtime errors that kept cropping up due to a dynamic type system. So many typos, nil propagation, subtly incorrect function application, confusing macros etc.
- elcct 11y agoI stopped after reading "reason for panic?" paragraph. Basically, if you don't like the tool or you are not too comfortable with it, don't use it. If you want to pass arbitrary stuff through a channel you can for example serialize to JSON or even use things like protobuf if you are religious about memory. If you really want to send something random, then there is something wrong with your project.
- drdaeman 11y agoIsn't very similar attitude "oh, why bother with checks, I'll never do anything wrong with those" is a case of most segfaults in C programs?
- mikelward 11y agoDid you not understand the func merge[T](cs ...<-chan T) <-chan T reference? The author doesn't want to send something random, they want to send things of a single type, where the type is defined at compile time, without re-writing functions like 'merge'.
- elcct 11y agoI think I was not clear - I mean essentially what you replied. I think this is still a try to apply mindset of one thing to another. You can't do generics in Go (well, you can use template system if you really want), so why would you try to solve the problems with generics in mind? It is like you were really upset that getting family in an industrial truck to Disneyland is not comfortable. If you have a problem that requires generics, use something else than Go.
- arnarbi 11y agoThe kind of channels* the article talks about are not delivery mechanisms, but primarily a synchronization primitive. They have little to do with serialization, and indeed "sending" references to data without having to encode them is critical in many cases. Besides, serializing data throws away type safety just as much as a catch-all type (in go, interface{}) does. * The naming "channel" is old: https://en.wikipedia.org/wiki/Communicating_sequential_processes https://en.wikipedia.org/wiki/Communicating_sequential_proce...
- hardwaresofton 11y agoThere seem to be a bunch of blog posts popping up recently criticizing golang channels (speed/ease-of-use advantages of mutexes, stdlib prefering mutexes, channels forcing awkward code, all the points in this post), and the lack of generics. I'm starting to think I might have drank the kool aid too early (grateful, however, to read something that caused me to reassess), as I have recommended Go at least once to coworkers. Is it fair to say that these are some of the last remaining warts of the language? I still like a bunch of other things about go (super ez cross compilation, package management system, fantastic stdlib & testing support)
- kasey_junk 11y agoYou like the dependency management & testing stories of go? I'd say those are pretty common universal complaints from the people I know programming go.
- hardwaresofton 11y agoAre you referring to vendoring? That's where I usually see the most complaints -- I mean the fact that something like "go get" was baked in from the get-go (to be fair, just about every modern language no considers this important). Not all languages came with any pre-baked (and decently considered) As far as testing goes, I find the stdlib to be adequate, especially for relatively simple programs. Are the complaints your familiar with over the lack of more powerful testing tools like quickcheck?
- hardwaresofton 11y agoAlso I wanted to add, if you are having problems with testing in golang, I've found great success with Gomega, Ginkgo and Agouti. They are fantastic libraries
- spriggan3 11y agoAnd then some guy somewhere will tell you using third party testing libraries is not "idiomatic" and shame the maintainer publicly in a blog post like it happened before. "You don't need that in Go"™
- blablabla123 11y agoHigh level abstractions are nice and I see myself rewriting certain patterns. But that's not only true for concurrency patterns, for object oriented patterns as well. Still most people write these things each time by hand, even if some language have baked in support for simple patterns like Delegate. I'm actually quite fine orchestrating concurrency with channels and the sync package but I use Go since 2011. I suspect that people new to the language are tempted to write things more complicated than they really are. I understand that it's difficult to find out when to close a channel. But in reality, when it's complicated figuring that out, probably channels aren't used optimally. Regarding Generics, there have been plenty of discussions on the Golang Google Group. Keeping the language lean and consistent also seemed to be the main priority.
- TheHydroImpulse 11y agoIf people are still writing it by hand each time, it's a problem with tooling, specifically dependency management. If it's that much of a pain to distribute a library, then people are going to write it themselves. That's not a reason not to include generics, however. It's an entirely different problem.
- alpb 11y agoThis content is widely discussed 2 years ago: https://news.ycombinator.com/item?id=8315996 https://news.ycombinator.com/item?id=8315996
- kasey_junk 11y agoAnd yet, the complaints stand.
- jerf 11y agoGiven the generalized complexity of getting sync stuff done correctly, I'm not going to try to bash this out fully in an HN comment, but you can do less-boilerplate Promises (in the sense the term is used in the blog post) by using composition instead: type Promise struct { *sync.Condition err error finished bool // note no "payload" here with interface{} } func NewPromise() *Promise { // return a new promise } func (p *Promise) Wait() error { // code here to use the condition and bool to wait, or get the error } func (p *Promise) Finish(err error) { // code here to mark the promise as finished or errored } type AccountPromise struct { *Account *Promise } func GetAccountPromise(f func() (*Account, error)) *AccountPromise { promise := &AccountPromise{nil, NewPromise()} go func () { var err error promise.Account, err = f() promise.Finish(err) }() return promise } The boilerplate would only be that last function. The promise is used as: err = accountPromise.Wait() // accountPromise.Account is now valid if err is nil That's the reflect-free version. If you're still feeling crabby, I believe you could safely reduce the boilerplate even farther with some reflect usage. (By "safely" I mean that you can statically guarantee the loosely-typed reflect usage would still be correct.) That can be used with arbitrary callers of .Wait(), without an explicit queue (shared lock for the condition functions as the de facto queue). Though I still would ask serious questions about Go code that has extensive use of "promises". Generally I'd expect there's somewhere that sync.Once could be used, or some other solution native to Go rather than porting in promises. You don't really need promises in Go; for instance, to head off the objection that I'm not showing how to do any sort of ".Then()" function so this isn't "real" promises, Go's code "color" [1] is already fully asynchronous, so you don't need the usual array of Promise combiners to create a new function color. Go is already basically promise-colored itself. Not that it's perfect. Generics to put together useful channel patterns would be legitimately useful. I've missed that several times. But the composition is generally underrated in Go. The programming community has decades of experience in inheritance-primary language, but composition-primary languages afford a different style, even if both are OO in some sense. [1]: http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...
- pcwalton 11y ago
- tshadwell 11y ago> How to make it reusable? `<-chan interface{}`? Welcome to the land of types casting and runtime panics. If you want to implement high level fan-in (merge) you’re losing type safety. The same (unfortunately) goes for all other patterns. I can understand arguments for generics, but they're complaining about trying to warm a whole pizza with a toaster. You need to put individual slices in, it doesn't work like an oven. Cook it with an oven if you want, but don't try to make it out like the toaster is offensive and useless. Yes, you might need to re-implement these patterns each time with separate types (slice up the pizza), but it's not really a lot of work for the extra speed and crunchy pizza it gives you. I'm pretty tired of the frequency of these articles where someone takes a concept from a language, tries to force it into a different language with little care for its idioms and then complains that it doesn't work. Maybe I like digging holes with a trowel, maybe I like the precision it gives me, maybe I've worked out a way to with a little extra effort accomplish the same work. If you work with a spade and a bucket all the time don't complain that you can't throw a trowel around like a spade.
- eknkc 11y agoLanguage idioms are not set in stone (mostly). You have functions, variables, containers etc there. It's not like they invented a whole new way of computing. Not unreasonable for people to ask for some decent ideas that solve problems in other similar languages. Like generics. Maybe if enough people complains about their toaster's pizza heating capabilities, manufacturers would release pizza heating toasters. Or at least explain why, technically, it's not a good idea. Not yell people "you would burn down your houses! idiots! we are looking after you." At least it feels like that to me, the condescending tone of designers / advocates is irritating in Go land.
- tshadwell 11y agoAll understood, and you're right, but the tone of this article and that of its kin is at least equally bad at idea discussion -- it's not weighing out positives and negatives or trying to get an idea of why things are how they are, it's just angry that it can't do the thing it wants to do, and it wants you, and everyone else to know. I'm saddened that you feel like the Go community doesn't answer these questions, and I'd love to write an article on why these things are how they are and how they can be useful, but that's beyond the scope of a HN comment. Yes, I disagree with the author of the article, but the primary thing I intend to satirise is his angry tone. If you've felt that the Go community has been condescending, I hope the condescending articles get equally as many critics.
- avitzurel 11y agoBeyond Go and channels, this article/post touches on a subject that is personally a real pain to me. > It’s so cool to find yourself writing tutorial for beginners! And it's a bit painful when you are trying to implement big and sophisticated system(s). Channels are primitives. They are low-level building blocks and I highly doubt you want to work with them on daily basis. Posts/talks that relate to beginners. You know the drill, you only have a limited time to talk about something in a conference and you only have limited time to write a blog post. It's easy and tempting to cover the basics without diving in too much into the details. What you are left with as a professional is very thin data on how to accomplish complex stuff. If you look at Docker and all the talks/posts around docker, they cover the absolute basic. Where's deployment to production? where's alerting/monitoring? One of the few people I see solve this problem is: http://patshaughnessy.net/ http://patshaughnessy.net/. I've been trying myself to do more in depth posts that cover more than just the basics, it's really hard work.
- divan 11y agoThis post is from Sep 2014. I know the author, he's a nice guy, but when he was at Golang Meetup, he was desperately trying to convince people to switch to Clojure. It's all you have to know about the bias of this article.
- eternalban 11y agoCurious: Can you expand on his "desperation"? Handing out money? Begging on his knees? Giving away little fluffy stuffed animals? A promise of a date with Rich Hickey?
- divan 11y agoHah. "Begging" is close :) He wasn't a speaker, but asked for a mic and started 15min speech for the newcomers, explaining that "Go has no immutable types", "channels cannot solve all the problems", "don't use Go, switch to Clojure" and so on. He also made up some example of board game, as a proof that language without immutable types is flawed and channels are not enough. It was pretty awkward, really.
- bsaul 11y agoThat's funny... Usualy those types of people are just a bit crazy and unable to make any kind of descent reasonning. But in that case i found the blog post pretty in-depth and well said.
- kasey_junk 11y agoI don't want you to switch to Clojure, I program go full time everyday and I largely agree with the article.
- placeybordeaux 11y agoI would have much prefered the title Golang: I still want generics This post has little to do with channels.
- kasey_junk 11y agoYou aren't wrong, but the strongest arguments for generics in go (and in many other languages) have to do with the abstractions around concurrency. In a language that supports generics you have a fighting chance of adding good concurrency pattern support above and beyond what the designated language gatekeepers designate. In go, you have to resort to codegen.
- spriggan3 11y agoPeople need to stop trying to force themselves using Go. Yes, good concurrency perks and easy deployment are handy, but Go basically demands people to code 1993 C-Object style since its authors basically rejected 20+ years of type theory. If it feels dirty don't promote that, don't promote a community that is in total denial when it comes to its language shortcomings. Plenty of alternatives like D,Nim,Vala,Crystal,Erlang and many more. You want the same developer experience as Go (go get,gofmt,golint,go-code...)? then develop the tools for these languages and drop Go. Don't settle for an half baked language just because "it's easy to start with",it's easy to start with because the standard library is dumbed-down. Just look at Go projects on Github where developers spend their time trying to fix the Go std lib in their own code...
- akavlie 11y agoA lot of people are not free to decide that. Go has become very popular in young, mid-size tech companies. Most of the alternatives you listed are too new/fringe/unproven to consider. And, well, they're not made by Google (corporate backing makes a big difference with new languages).
- geodel 11y agoIt is also getting popular with IBM, Comcast, RedHat and of course Google etc which are older and big companies.
- stcredzero 11y agoYes, good concurrency perks and easy deployment are handy, but Go basically demands people to code 1993 C-Object style since its authors basically rejected 20+ years of type theory. If it feels dirty don't promote that, don't promote a community that is in total denial when it comes to its language shortcomings. What languages are popular and what languages are people getting stuff done in? Maybe the people in denial aren't the ones supposedly denying the supposedly "better" paradigm/model/type system? Maybe the ones in denial are the "misunderstood" languages? For our young field of programming, this now qualifies as an old story: A "superior" computer language is misunderstood by the mainstream, and progress is tragically held back. It's happened before, and it will happen again. But I posit it would be better to think of each language as an iteration and to look for lessons learned. As someone who spent time in such a language community, let me point out one such: Technology and paradigmatic superiority are only two ingredients out of many that contribute to the success of a language community. Do outreach. Make your stuff "just work" for the beginner. Be able to solve real problems while not creating too many new ones. Also mind the overarching lesson of all software history: Pragmatism wins the day.
- tedsuo 11y agoA point often missed by new Go programmers is that channels are only for select. In other words, channels are a primitive for handling nondeterminism at the runtime level, not a general purpose queue. If there is no select statement, there is no actual need for a channel. A well designed interface, not a primitive, is the correct way to express an Object or an Abstract Data Type in Go. Specifically, prefer a Queue or Collection interface over a raw channel. (Unless you need to use select.) This is our fault though, for putting channels in every beginner example, while never really explaining select properly.
- avz 11y agoI used to miss generics in Go, but after a few years of coding in both Go and Java I recognize the costs, but also the benefits of their absence. We use generics to define new abstractions. This allows us to reduce redundancy in code and provide succinct way of expressing complex concepts. The main cost of not having them is that we have to fully write out the algorithm more often. At some point I realized that when I read someone else's Java code I often find myself digging through countless layers of abstraction, factories, builders, suppliers, visitors, delegates and each each time I have this thought in my mind: "so where the hell is this place where the stuff actually happens?" In Go, this place is right there inline in front of you or very few hops away. This is particularly important if you're trying to understand a corner case, e.g. how does this function/class behave when I give it an empty list or null. Faced with the choice between having to understand an abstraction or an algorithm I prefer an algorithm. Most mainstream languages have generics. I'm happy there is one that does not. (Disclaimer: I am by no means a Go apologist: I have very mixed feelings about the language overall. One thing I was hoping would be discussed in the article was that channels don't compose well with other concurrency primitives, e.g. one cannot select on a channel and a condition variable) EDIT: formatting
- karmakaze 11y agoTL;DR > channels don't compose well with other concurrency primitives
- groovy2shoes 11y ago> At some point I realized that when I read someone else's Java code I often find myself digging through countless layers of abstraction, factories, builders, suppliers, visitors, delegates and each each time I have this thought in my mind: "so where the hell is this place where the stuff actually happens?" This has nothing to do with generics and everything to do with Java's heavy-handed approach to OOP.
- acjohnson55 11y agoI think I fat fingered the down vote button. Sorry. I completely agree with your point.
- logingone 11y agoWhy isn't Go the same as the other languages? Because then it would be the same as the other languages.