Live data from Hacker News

What color is your function? (2015)

journal.stuffwithstuff.com

31–40 of 198 posts

Re: What color is your function? (2015)

#31
post #24
post #6

> You still can’t call a function that returns a future from synchronous code. (Well, you can, but if you do, the person who later maintains your code will invent a time machine, travel back in time to the moment that you did this and stab you in the face with a #2 pencil.) Author makes up a lie. Then lampshades it away with a colorful non sequitur. --- The alternatives that people praise like golang, have other trad…

Your entire codebase is already at risk of being blocked by a spinlock or CPU-intensive operation, so what's the difference?

If you haven’t taken a lock, any other code can start executing at any time, so any invariant you might have established on one line may no longer be true on the next line.

If you don’t depend on anything mutable that anyone else can modify then this is mitigated, but that’s a very specific discipline you have to abide by.

Re: What color is your function? (2015)

#32
post #10

I wish the key word was instead dontawait and was used inversely to how await is used. 99% of the time I'm using an async function, despite however slow it is, there's nothing for my code to do but wait for it to finish. But if for some reason I would like the next line of code to run before the current one is done, I'll let you know . Like, why can't my sync function await something asynchronous? If it has to lock u…

At least in JavaScript, it's nice to be able to see explicitly where you can expect the function to yield, so it's clear when race conditions can occur, or if you're calling it in a loop, whether you should consider running things in parallel.

Plus, you probably don't want to lock up the whole thread if you're writing anything more than a quick script, like a web server or a GUI.

Re: What color is your function? (2015)

#33
post #29

Earlier quoted context omitted.

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

That's mostly because BEAM uses an actor-style approach while predating the concept of actors, isn't it? Interesting artefact of history if so Edit: upon rechecking, apparently that's not exactly right, and Erlang designers learned of actors after designing the language, which makes it all the more interesting

Actors predate both Erlang and BEAM by significant factor

Re: What color is your function? (2015)

#34
post #25

Earlier quoted context omitted.

> Like, why can't my sync function await something asynchronous? The answer, at least for Python, is that it is an intentional limitation because the alternatives introduce some quite bad trade-offs. Option 1: your awaited promise goes into the main async event loop. This is bad because it means that your single-threaded sync function now needs to be thread-safe, and so does any sync code that calls your sync functio…

I think the downsides of option 2 are overstated here. In lots of cases you don't care about the "value of async", you just want code that works well enough and option 2 does accomplish that in anything that is not perf critical.

I agree in isolation, and I have used nest-asyncio a couple of times where it really was a lot easier than the alternative, but from an ecosystem perspective I'm glad it isn't the default. Most of the time someone wants to do this it's a junior trying to work around a non-issue (e.g copy-pasting from a guide that includes asyncio.run()), and the trade-off is a massively increased surface for performance footguns throughout your code base and all the libraries you use. Linters could save you from the first case but it would be a lot more work to profile, track down, and fix spots in all your dependencies that cause your event loop to get fragmented.

Re: What color is your function? (2015)

#35
I feel like this argument always boils down to explicit vs implicit. It tastes the same as static vs dynamic typing. Personally, I fall well into the explicit camp. I like when I can know stuff about a function without having to read its body, and the bodies of the functions it calls, and the bodies of the functions they call, and so on. And so, I like when I can see from the function signature that it returns an integer, or when I can see from the function signature that it might do IO.

This comes at a cost, namely that of reading five extra characters in a function signature, and I could kind of imagine (truly!) how that gets in the way for some people. There is a cost of writing the five characters as well (and like the author mentions, in a poorly designed codebase, this may have to go down the call stack), but code is read more often than written, so in a sense this is negligible.

Like the dynamic vs static typing debate, I feel like this ultimately boils down to context and personal taste, and some amount of intelligence as well. I'm impressed by the amount of stuff the dynamic typing / non-async crowd is able to keep in their working or long term memory while coding. I don't have that kind of mental bandwidth, sadly.

Having said all that, this argument is disingenuous in that it completely ignores the fact that the async keyword tells you something useful (rather than some made up nonsense like color), and most of the argument basically boils down to "if you ignore the benefits, this syntax has no benefits", and I really don't respect that as an argument.

Re: What color is your function? (2015)

#36
post #16

Colored functions are good. It reflects the language design on signaling what is important, and what are the properties it want the writer to pay attention. Other examples of colored functions: * Haskell: pure function and non-pure (IO monads) looks different. * Rust: unsafe functions (or block) requires special markers.

Rust unsafe functions aren't a good example of colored functions because it doesn't exhibit the main issue brought up in the article, that one color can call the other but not the other way around.

In Rust, unsafe code can call safe code, and safe code can call unsafe code. Calling unsafe code in safe code requires an explicit unsafe block, but that's fairly normal and not a hack to get around function coloring.

A better example could be Rust async, though unlike JavaScript, you have the option to block the thread on an async function in a sync function.

Re: What color is your function? (2015)

#37
post #30

I really don't like this article. It has a catchy, profound-sounding title that people bandy about to argue against stuff they don't like. All functions, even non-async functions, are colored. In any large system codebase you'll have functions that can only be called in certain situations, with the right setup, whatever, and if you're lucky this is communicated by types but regardless those restrictions can't be avoi…

Every time this is posted, it’s worth reminding: async functions in JavaScript are the correct design, and the people who did it deserve praise.

Re: What color is your function? (2015)

#38
post #30

I really don't like this article. It has a catchy, profound-sounding title that people bandy about to argue against stuff they don't like. All functions, even non-async functions, are colored. In any large system codebase you'll have functions that can only be called in certain situations, with the right setup, whatever, and if you're lucky this is communicated by types but regardless those restrictions can't be avoi…

Yep. It's not an async vs not async thing. The way some people talk about it, you'd think the async keyword was at fault. It's all about whether a function is callable in some context.

Passing in the context as an argument or making it a global variable or returning a monad doesn't do anything to uncolor the function. What's the difference between `async function f()` and `function f(eventloop, callback)`? Only syntax.

Not to mention there's lots of colors unrelated to async, that most languages don't type at all. And if you use the wrong one, your program just doesn't work correctly at runtime. Thread-safe vs thread-unsafe. Blocking vs non-blocking. May throw/panic vs won't throw/panic. May fail/return null vs infallible.

Re: What color is your function? (2015)

#39
post #15
post #3

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 :-)

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 i…

That's just not true. Let's say you have a form validation library with a public api that supports custom validators Validate(name string, value string) bool. Then you decide that your validator now needs to make an HTTP request. This request needs context so that tracing is propagated and needs to return (bool, error) so that error is propagated up instead of silently ignoring it or logging it and returning false. This is coloring. You can use context.Background the same way you can use blocking in other languages. It just doesn't feel right and it breaks things.

Re: What color is your function? (2015)

#40
post #6

> You still can’t call a function that returns a future from synchronous code. (Well, you can, but if you do, the person who later maintains your code will invent a time machine, travel back in time to the moment that you did this and stab you in the face with a #2 pencil.) Author makes up a lie. Then lampshades it away with a colorful non sequitur. --- The alternatives that people praise like golang, have other trad…

> Your entire codebase is now a surface area that is at risk of being blocked The point of goroutines is that they can freely block when needed. It's not like async where you have to be paranoid at every moment about writing blocking code

Why would there be paranoia when writing blocking code with async?

The downside of goroutines is that you have no control when the goroutine context switches, so naively accessing a global value can lead to race conditions (which the language has no warnings for despite being such a concurrent language), while the same code works fine in JavaScript because context switches don't happen in synchronous code.

Post reply on HN