logoalt Hacker News

nickcwtoday at 4:59 PM3 repliesview on HN

Go doesn't have colored functions due to its nice fat runtime hiding all the async magic away for us.

That makes it a pleasure to code concurrent stuff for IMHO.

It does have its own similar problems though - does a function return an error? If so you are going to need to plumb the error return through all the callers. Does a function need a context.Context? Ditto.

I guess you can't win them all :-)


Replies

seabrookmxtoday at 5:52 PM

Same with the BEAM languages like Erlang, Elixir, and Gleam. Though it still bothers me that they call their green threads "processes".

show 1 reply
vqtoday at 5:11 PM

And Haskell is an ensemble of rainbows. It's very fun and pretty to look at.

Type classes can smooth over some of it but it's not unusual to have to do some plumbing.

jerftoday at 5:35 PM

This is a subtle point that I've seen missed repeatedly, but: The reason that "color" is important is that if you have a function ten layers down in your stack that is the wrong "color", you now have to change that top-level function. There is no other option.

Propagating errors up the stack is not the same, because the top-level function is not developing an error return because of the 10-level-nested function. It is developing one because the function it called has one, and apparently, it needs to return it to its local caller. It's a local consideration. It is true that it may be a recursive local consideration where this was true 10 times, but the reason it is different is that it doesn't have to be that way. It could have been the case that the function 7 layers down handled the error somehow and it stopped propagating up the stack. But at each point, the consideration was local, and as such, amenable to local solutions other than just tossing the error up. If you choose to "correctly" plumb the error through all your functions, well, good on you for apparently being willing to apply good software engineering practices even when it's annoying, but this is just normal day-to-day function activity stuff.

By contrast, in a function coloring situation, if the color is wrong 10 layers down, you must change the calling function. It's a non-local consideration. You don't get to decide not to change it. You can't encapsulate it. You don't get a choice. It pollutes the entire stack, forcibly.

Another way to look at it is, if the function 10 levels down developed what you think is a color, but there is a way for the function 9 levels down to hide the color from the rest of the stack, even via a hack like simply dropping an error you really need or hackily constructing an object of some type to pass in, then it is by definition not a color. A color change can't be stopped by any way of writing an intermediate function. It must be propagated all the way up the stack.

If you don't have this, you don't have "color". Like, some people will say that in their language that maybe there is some way to encapsulate "async". If you can, then you don't have an async color. Although I will say that if your "encapsulation" is basically to run it in a non-concurrent environment, that's really not encapsulation. It isn't really "encapsulation" if you're giving up an entire major feature of the language, because that is something very visible to the rest of the program.

Go's context.Context is similarly not a color. You can always just create a context.Background() and pass that down. If you didn't have any context already in hand, which means you must not care about any of the features context offers, then that is usually a fine thing to do. Context is trivially bypassed if you don't want it. It can be encapsulated within a portion of the stack without "polluting" the rest of the stack like any other function parameter.

The key aspect of color is that it is not optional. It isn't something that you can just decide to ignore and stop passing up, or trivially create a value for passing down to other functions. You have to change the "color". Async is a color in many environments. There aren't really that many colors in programming languages because they are very, very quickly inconvenient and we tend to squeeze them out. (Haskell really sticks out here as a language that is not only capable of creating arbitrary colors, but where this is an explicit tool used by the community rather than a limitation, and they even have ways of combining colors together deliberately.) Statement versus expression distinctions are another one, where a "statement" may not be usable in an "expression", and you'll note how languages have in general erased that one over time because it's really just a cost without much benefit.

show 1 reply