Hacker News new | ask | show | jobs
by Zach_the_Lizard 1066 days ago
I have written Go professionally for many years now and don't want to see it become something like the Python Twisted / Tornado / whatever frameworks.

The go keyword nicely prevents the annoying function coloring problem, which causes quite a bit of pain.

Sometimes in high performance contexts I'd like to be able to do something like e.g. per CPU core data sharding, but this proposal doesn't scratch those kinds of itches.

6 comments

Coroutines and goroutines fill different niches. The latter already fill the niche the likes of Twisted fill. There's nothing here trying to pull anything akin to async/await into Go.

Coroutines will fill a different nice more akin to Python's generators. There are a whole bunch of places where this could dramatically cut down on memory usage and code complexity where you have a composable pipeline of components you need to glue together. The design appears to me to be entirely synchronous.

I have had some experience with wire protocols that might benefit from coroutines. If I use goroutines, I have to use sync or channels, and then be careful. If I use state machines, it can be cumbersome. I suppose we will see.
For sure cut down code complexity for the single clever code writer who wanted to be fancy. Not so sure about all the readers that will come afterwards.
Not at all. I often have to slurp in large amounts of data to be processed in some way. If you don't want to use huge amounts of memory for doing so, you either need to (a) write some horribly obtuse code or (b) use coroutines. Coroutines allow you do this in a more natural and composable way.

Anything involving a lexer and parser is a good example of this, and exactly one of the use cases given in the example. With coroutines, the lexer can take some input and emit a series of tokens which the parser can consume on the fly without any need to do any complex interleaving of the parser and lexer code. You also gain the ability to stop lexing early in case the parser encounters an issue.

I'm not sure what kinds of software you work on, but this is little more complicated or clever than Unix shell pipelines.

Isn't this literally the entire point of Golang?
> The go keyword nicely prevents the annoying function coloring problem, which causes quite a bit of pain.

The good thing is, nothing in the post even proposes anything that might fall in the function coloring problem.

> The go keyword nicely prevents the annoying function coloring problem

FYI when you write a goroutine and have to use Context, that's literally just function coloring

Ad absurdum, then anything other than full curried 1-ary-exclusive functions is coloring.

You can easily bridge context-using and non-context-using code, you can have nested contexts, you can have multiple unrelated contexts, you can ignore the context, probably most critically you can't pass values back up the context, I don't see any way these look like function coloring.

Not sure about in Python, but in Rust, you can easily bridge sync and async code. The function coloring problem is, at least in that case, grossly overstated.
Why was this downvoted? The person makes an interesting point.

For discussion, can you please provide a short sample in Rust to demonstrate your point? I would like to hear more.

I'm guessing it was downvoted for mentioning Rust ¯\_(ツ)_/¯

For sure - it's a little terse, just to demonstrate the various cases

  async fn do_something_async() {
    nonblocking_sync_call();
    tokio::task::spawn_blocking(blocking_sync_call()).await;
    async_std::task::spawn_blocking(blocking_sync_call()).await;
    async_call().await;
  }

  fn do_something_sync() {
    nonblocking_sync_call();
    blocking_sync_call();
    futures::executor::block_on(async_call());
  }
There is _a bit_ of ceremony required to bridge the two, but it's pretty minimal.

If you want to know more about any of that, happy to explain or provide links for further reading

Is it really? >90% of the time, I'm just passing a context so that I can cancel the rest of the goroutines if an early one fails and I don't need to do the rest of the computation.

That works just fine with throwing a context.Background in, which should be available anywhere, and not color the function.

Arguably the "problem" with other async/threading frameworks is that they build threading on top of iterators/coroutines. In this case they would be orthogonal, so it's probably not as bad as you think, though almost certainly some clever developer out there will do something stupid with it that will also get very popular (my bet would be abstracting goroutines and coroutines into a single thing)
Can you maybe share hints to better channel management patterns/frameworks?

Usually my goroutines related code feels like a mess because of them, and I don't know how to make them "more clean" (whatever that means). Any hints proven to be maintainable patterns would be highly appreciated.

I don't always use channels in the code I write.

For example, let's say I have []foo as input and I'm going to call a service on every element of the slice.

You could do it with channels, but oftentimes I'll reach for creating a slice of results and passing each goroutine an index into the slice. That way I can avoid any overhead that comes from having what is effectively a queue with a lock.

It depends on what I'm doing, how I want to treat failures of a single goroutine, etc. of course.

I default to never returning channels unless it's a requirement for the use case (e.g. context.Done() returns a channel so callers can listen for when the context is closed).

Callers can always create goroutines to make your code run concurrently. It's more annoying to erase channels when they aren't useful to the caller (e.g. if you made all functions that make HTTP calls return a channel because you assume callers want a new goroutine, but I already have my own goroutine per request)

Beyond that, some specific examples would be helpful so I can tailor my advice

Don't worry, it will get even worse, because its foundations are worse than pythons - and it also has less flexibility because it's not interpreted. Like it or not, it's going to happen. I also predicted that generics would be added to Go and folks here laughed about it. :)