9 ms·
All of those interfaces are trivial to implement in Go precisely because Go implements a much stronger abstraction. By contrast, if you only have those other ab
by wahern 8y ago
All of those interfaces are trivial to implement in Go precisely because Go implements a much stronger abstraction. By contrast, if you only have those other abstractions you're quite limited in how you can structure your code. (You might not realize how limited you are, however, if those are your only options.)
As the article says, most languages settle for those interfaces because they can mostly be implemented as libraries with minimal refactoring of the existing implementations and their execution model.
They have their uses but they're not universal enough. If you think actors, futures, and async/await are so great, imagine having to write every single function invocation in that manner. It would be unimaginable outside of some special-purpose declarative DSL. By contrast, the concept of a thread--linear flow of logical execution of statements and expressions--is fundamental to programming. Even in functional languages with lazy evaluation or automagic parallelism. It's a basic building block similar to functions. And much like functions, it's incredibly useful to be able to easily compose those blocks, which is where channels and message passing come into the equation. One way to compose them is with actor and future interfaces, but that's not the only way and not always the most appropriate way.
Threads ended up with a bad name because of (1) performance and (2) races, but that conflates the abstract concept of a thread--a construct that encapsulates the state of recursive function calls--with particular implementations. Goroutines are threads, pure and simple, but [mostly] without all the baggage of traditional implementations. (That they can be scheduled to execute in parallel complicates the construct but also makes them useful for the same reasons traditional OS threading constructs were predominately used, except with much less of the baggage.)
- GordonS 8y ago> By contrast, if you only have those other abstractions you're quite limited in how you can structure your code. OK... but what languages only have those abstractions?
- wahern 8y agoI think it's fair to say that neither JavaScript, Perl, nor Python, notwithstanding Web Workers or rough POSIX threading support; but Lua and Scheme do even though their threading construct is not based on the system threading model (though strictly speaking it's likewise available to those languages). What I really meant to get at was that Go provides a flavor of threading that is lightweight, simple, and ergonomic. The threading construct is a first-class citizen and fundamental to both the language design and its implementation tradeoffs. Threading, lexical closures, and GC were designed and implemented holistically. Go takes a performance hit for its seamless support of closures (lots of incidental heap allocation), for example, but they did it because notwithstanding the Go authors' utilitarian goal it was important that the core abstractions remained relatively unadulterated and natural. If this wasn't the case (if it was just about avoiding manual memory management), Go could have required explicit capturing of lexicals, like C++ and Python do. People complain that Go doesn't have a lot of sophisticated features, but that's because everybody is focused on typing and generics. But modeling execution flow is at least as interesting academically and important commercially. While Go seems simple, supporting these constructs the way Go does is actually really difficult. Which is why it's so rare to find. I intentionally didn't mention channels because while syntactically nice the real internal complexity (and payoff) comes from the closures. Channels are something of an implementation detail which you can easily hide inside closures in a functional-style of programming; threading + closures allow you to implement coroutine semantics very cleanly (i.e. no async/await necessary) and, if you so desire, transparently. (And it just occurred to me that async/await is such a misnomer. In a coroutine caller and callee are logically synchronous. The fact that languages like Python, C#, and C++ use async/await terminology shows how they put the cart before the horse--these constructs were designed and implemented for the very specific use case of writing highly concurrent async-I/O services, and they stopped at making that use case moderately convenient. They're a very leaky abstraction. See function color problem mentioned in the article.)
- GordonS 8y ago> I think it's fair to say that neither JavaScript Ah yes, how could I forget Javascript :) > The fact that languages like Python, C#, and C++ use async/await terminology shows how they put the cart before the horse Of these, I'm very familiar with C# (15+ years' experience). C# had great threading constructs long before it added async/await, and still does. It actually provides a great variety of threading constructs - you can go low level with mutexes, wait handles, and threads; then there's the Task.Run abstraction, Parallel.ForEach, the TPL... When async/await in C# was first announced, it was hailed as making concurrency much simpler for devs, but I've always found threads much simpler to reason about and debug, while async/await gives you a variety of footguns that can be difficult to debug. FWIW, I've been using async/await in C# for years now, but coming from much more of a threading background, I confess it's only now beginning to feel intuitive. I dunno, maybe if new devs come to concurrency from the async perspective first, it's easier to grok.
- kasey_junk 8y agoThe biggest problem with go is that it’s not easy to implement those as libraries. This is a combination of the golang story around generics & their opinionated strategy on concurrency. Conversely most other modern, mainstream languages can mimic golang concurrency as a library.
- wahern 8y agoYou absolutely cannot mimic Go concurrency. You're not understanding (or not appreciating) the function color problem mentioned in the article. See, e.g., http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...
- kasey_junk 8y agoOr it’s possible I don’t agree with its premise that go solves the problem better than other languages because it hides async behaviors from the type system. In practice it doesn’t. Asynchronous functions leak into golang implementations in worse ways in golang. Everything from the near universal use of channels as poorly implemented promises to the horrendous Context being passed to everything for cancellations. The golang concurrency story is weak compared to any language that has a story at all.
- apta 8y ago> If you think actors, futures, and async/await are so great, imagine having to write every single function invocation in that manner. I don't see how: val f = Future { foo() } // do work... f match { case Success(res) => useRes(res) case Failure(error) => handleError(error) } Is any more difficult than: done := make(chan bool) var res int go foo(done, &res) <-done useRes(res) As a matter of fact, the first one is much easier. I don't have to create and pass a channel, and manually handle returning a success or failure. The first approach is much better
- wahern 8y agoYou're not understanding (or at least not appreciating) the function color problem mentioned in the article. See, e.g., http://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ http://journal.stuffwithstuff.com/2015/02/01/what-color-is-y...
- apta 8y agoI read that article before. Here's the thing, the only thing that the "go" keyword makes easier is having the ability to have any arbitrary function run in a green thread without having to modify it's signature. However, the moment you want to actually do something useful with it (e.g. communicate with it, cancel it, or read its returned value), you're going to have to pass a channel (or more) or a waitgroup to it anyway, modifying its signature and changing its "color". The issue remains pretty much the same.