5 ms·
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
by rendaw 5mo ago
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 avoided. It's easy to call low-restriction functions from high-restriction ones and not the other way around.
Furthermore, it's not like the alternative to explicit await doesn't have issues too (that the article doesn't mention). There is inherent complexity, it's a tradeoff, you can't just syntax it away.
- kubb 5mo agoEvery time this is posted, it’s worth reminding: async functions in JavaScript are the correct design, and the people who did it deserve praise.
- lmm 5mo agoAsync functions are an ad hoc, informally-specified, bug-ridden, slow implementation of half of monad support.
- moomin 5mo agoMaking await flatten promises was kind of questionable. But that’s my main beef.
- tikotus 5mo agoC# (and F# before it) got it (mostly) right. JS did a shallow copy of it, messing up some details, making it harder to use for certain things.
- hmry 5mo agoYep. 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.
- deepsun 5mo agoI believe a clearer example would be: `async function f(): Foo` vs `function f(): Future<Foo>`. Isn't it how it works inside anyway?
- eikenberry 5mo ago> 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. "Only syntax" is assuming, mistakenly, that syntax doesn't matter. Also there is a big semantic difference there.. that being in one case you have the flexibility of the passed in parameters taking different forms vs. the static 'async' statement. It is not strictly an async thing, but a general rule that additional keywords are less powerful than parameters in all cases. Ask any Lisp developer what the difference is..
- dnautics 5mo ago> What's the difference between `async function f()` and `function f(eventloop, callback)`? Only syntax. Negative. what is the "async prefix" equivalent of the following? global e: eventloop; noasync fn parent() childfn(e) end
- epestr 5mo agoAnte has some points on this issue: https://antelang.org/blog/why_effects https://antelang.org/blog/why_effects. All of this is just different syntax in other languages and solved but the abstraction provided seems to be neater.
- jcranmer 5mo agoWhat I like least about this article is that it's completely soured the entire context of asynchronous programming. Invariably, any time someone discusses design of an async functionality, function coloring is brought up, with almost no analysis as to how it applies and why it's a good or bad thing. (Ironically, I probably see more in-depth analysis these days as to why this article isn't apropos than why it is when this happens.) It's just reduced to "anything that makes a separation between async and sync is function coloring and that's automatically bad." The existence of any sort of trade-off, or really, the entire meat of the article, is just completely ignored. One thing that can be better called out is that this issue of function coloring isn't just an async problem. Exceptions cause function coloring--and not just Java's controversial checked exceptions. An infallible/fallible domain split is function coloring. Javascript's async handling is called out not because it's doing the function coloring but because--in 2015--the tools that existed for dealing with async code in JS libraries were really, really bad, largely reliant on callback hell. Promises and the async/await keyword fix most of the issues, and the ones that aren't fixed boil down to the fundamental issue that an asynchronous event-loop model and a synchronous batch model are just different programming paradigms to begin with.
- smcg 5mo agothis article seems like a nice view into what things were like in 2015 but we've come along way since then.
- dnautics 5mo agowhat I like least about this article is how people seem to just substitute some other, usually theory, notion of what function coloring is to elide the argument (usually to excuse their favorite PL) without actually RTFA. The article is about ergonomics, not PL theory. > Exceptions cause function coloring do they? Do they? 1) Every function has a color 2) The way you call a function depends on its color 3) You can only call a red function from within another red function 4) Red functions are more painful to call 5) Some core library functions are red
- deleted 5mo ago[deleted]
- amiga386 5mo agoI'm fairly sure the author was making reference to the famous article What Color Are Your Bits? https://ansuz.sooke.bc.ca/entry/23 https://ansuz.sooke.bc.ca/entry/23 which is even more abstract, because some large numbers are coloured "legal" and the same large numbers are coloured "illegal", based on where you got the numbers from. All functions are not coloured, don't try to wriggle out of it by generalising. This article is a specific complaint about Javascript. Javascript is a hack on top of a hack. Its async/await is crap. Javascript requires this "colouring" in a way that C#, Java, Go, Python, Ruby, C, C++, Rust, etc. don't, because they don't have to pretend they're a single-threaded event processing loop, while Javascript does.
- dnautics 5mo agoI haven't used python recently, but in the days of asyncio it was very much "painful" (to borrow the article's verbage) to use, precisely because of the five criteria in the article.
- sheept 5mo agoPainfulness isn't the main issue with colored functions, it only explains why we don't make every function red (async). The main issue is that sync functions can't call async functions, but in Python, you can bypass that restriction with asyncio.run.
- dnautics 5mo agoFta 4) Red functions are more painful to call I guess for me if I reach back to my memory my real problem with asyncio was that it used decorators and wrapping my head around how it was a crazy abuse of generators, completely broke my internal model of how python works (and also how at the time debugging became problematic), and maybe not so much the ergonomics, so strictly speaking a different set of ergonomic problems than in the colored function article
- pverheggen 5mo agoAgreed. Every time this article makes the rounds, people get hung up on the syntax, and not the performance considerations discussed towards the end. Nystrom wrote a whole book on interpreters, and his main criticism here is the big mess of chained closures this makes at runtime, compared with pausing/resuming green threads.
- moritzwarhier 5mo agoIt's an interesting repeat submission to study how HN comments change over time though. Regarding content, I agree with you. Async/Await is an amazing paradigm in JS for simplifying callback patterns and non-blocking suspense. In other programming languages, there exist other intriguing paradigms that are more elegant and emphasize other aspects of "async"; my prime example 2 would be Erlang, but I am not experienced in, for example, Rust or C#. The article has the same properties that many successful people IRL have: it makes a certain ick very easy to feel and understand, but it doesn't offer much in terms of profound knowledge. What it does offer though is a perfect spark of discussion, making people who, for example, only know the single-threaded async-await from JS, consider the sheer possibility of other approaches. I am among those people with a limited horizon, presupposing that "knowing" means deep experience to you. I have some superficial experience with Java physical threads, also with C#, but $job uses JS/TS. And even in JS, none of this is trivial in my mind. Consider the deceptively simple question of a kind of "mutex" that enables an async function or method to control concurrency of its own invocation. The answer to this simple question (queueing promises and clean rejection handling) is already far from trivial, involves the microtask queue, and shows where the mental model of JS-async-await begins to deteriorate.
- slopinthebag 5mo ago> It's an interesting repeat submission to study how HN comments change over time though. We've had at least a decade of using these async/await languages and discovered function colouring isn't a problem.
- deleted 5mo ago[deleted]
- soulofmischief 5mo agoWhile non-trivial in several ways, there are standard Web APIs such as Web Workers, Web Audio API, OffscreenCanvas, SharedArrayBuffer, etc. which help to construct modern, multi-threaded applications in JavaScript. Hopefully today, any experienced JavaScript-focused web developer should be experienced with more async paradigms than just async/await. I think these APIs address real issues, but it also makes the entire stack more complex when integrated language support might be better for some features. But, keeping things separate does mean JavaScript as a language is fairly portable and backwards-compatible, if you ignore Web API support. Still, a lot of other languages feel more batteries-included and have more elegant multi-threaded async fundamentals.
- jmull 5mo ago> All functions, even non-async functions, are colored. Not by the analogy laid out in the article.
- rtpg 5mo ago> It's easy to call low-restriction functions from high-restriction ones and not the other way around. do you have an example in mind when you say this? I think there's some unique messes with async/await (especially when combined with OOP and extension points... either your extension points _all_ have to be async or you have to have awkward restrictions), in a way that, say, permission checks don't have IMO. a syntax-less "await function" that climbs up the stack trace to whoever is waiting and holds onto the suspension _is_ a way out of the problem the article describes. Requires runtime support but it effectively means you can always suspend.
- johnfn 5mo agoSomething I have been thinking about recently is this: metaphors are often a way that authors use to make an argument in a way that is more engaging than: here is fact A, here is fact B, etc. But some metaphors are so strong that they make a stronger argument than the actual facts! And when you hear an argument with such a strong metaphor, you can often end up feeling very convinced of a particular point, even though the point itself isn't worth feeling so convicted over! I feel a very strong metaphor hijacks the emotional part of your brain before the rational part of your brain can figure out if it actually makes sense or not. In the case of this article, the metaphor is extremely strong. Colored functions! What could be dumber! You feel like you see the world clearly after reading the article, and you pity the people who can't see the clear categorization of functions like you can! But, in reality, when has this ever annoyed me? Never! I've worked with async functions in JS/TS for over a decade and there hasn't been a single time in my entire life where I've been frustrated or backed into a corner because I wanted to call an async function from a sync one. At worst I've had to tack on "async" to a couple function declarations and be on with my day.
- sqquima 5mo agoI do find it annoying. Let's say in JS I have `result = list.map(f)` but now `f` returns a Promise. `result = await Promise.all(list.map(f))` is less pleasant to read. And before writing it, I have to think if I want the `f` function to execute concurrently across all entries of the list, or one at a time: `for (const elem of list) { await f2(elem) }`. Or maybe I should use a library like `p-map` and carefully set the concurrency level. Or maybe I should create a bulk version of `f` that takes an array and is more efficient than calling `f` N times. And don't get me started when there's `list.forEach(f)` and `f` becomes async, so now it executes concurrently for all elements, and the engineer who made the change didn't realize it. And then there's Async Generators ...
- lmm 5mo ago> I have to think if I want the `f` function to execute concurrently across all entries of the list, or one at a time: `for (const elem of list) { await f2(elem) }`. I'd consider that a positive rather than a negative. That's an important question to think about (usually) and I want the type system to help remind me.
- moralestapia 5mo agoI agree it is a dumb article. It made a big fuzz back then but to me it's excellent as a litmus test. Learning async grammar in Javascript takes you an afternoon, after a week it should become familiar. If someone is unable to grasp that ... well, that tells you where they stand.
- wiseowise 4mo ago> I agree it is a dumb article. It is a great article. Just because you misinterpret it doesn’t make it dumb.