8 ms·
Long-time JS/TS/Node programmer here. Knowing ahead of time which functions are async is a feature. It's a big neon sign that says "hey, this function call is
by vlucas 2y ago
Long-time JS/TS/Node programmer here.
Knowing ahead of time which functions are async is a feature.
It's a big neon sign that says "hey, this function call is expensive". This is a good thing for programmers to easily see and know at the call site.
If you make multiple calls with async/await in a row, the performance issues are plainly obvious at the call site. With "colorless" functions, this information is hidden in a deeper layer. You have to know what the function does on the inside to even know what its performance impacts are.
Also, a nitpick - you can call async functions from sync ones, you just can't access the return value. Sometimes, you don't need to.
- mananaysiempre 2y ago> It's a big neon sign that says "hey, this function call is expensive". IMO the more important part is that it can yield control. Half the advantage of cooperative multithreading is knowing where you may yield control makes sharing memory more or less workable. I also like Trio’s model: if it’s async, it will yield control, even if the result is already available so it doesn’t technically have to. Makes it more difficult to accidentally starve other tasks. (I suppose I also have to mention Curio’s model: if it’s async, it’s a scheduler upcall, whether yielding or not. But for all that Curio deserves credit for freeing Python from asyncio hell, I just don’t find myself caring about things being scheduler upcalls or not.)
- newZWhoDis 2y agoRuby/rails is full of shortsighted crap like this. I feel stuck, our whole backend is legacy rails and I can’t escape.
- goatlover 2y agoBit of a strong claim when the Erlang/OTP was designed to handle massive concurrency without colorful methods. Given that both Erlang and Ruby are inspired by the message passing semantics of Smalltalk.
- lmm 2y agoUnicoloured languages are great as long as your code doesn't have to actually do anything (which is lots of modern code, to be fair). Try writing a physics simulator or 3D renderer in Erlang and see how that goes.
- pmontra 2y agoErlang is not great at math performance, also because it uses arbitrary length integers. There is a nice comparison between several languages, including Erlang at https://stackoverflow.com/questions/6964392/speed-comparison-with-project-euler-c-vs-python-vs-erlang-vs-haskell https://stackoverflow.com/questions/6964392/speed-comparison... It all depends on how the code is written. Eventually somebody managed to make the Erlang code faster than the baseline C, then someone else made the C version 8k % faster, which proves your point. However, how is that related to using sync/async vs message passing?
- lmm 2y ago> Eventually somebody managed to make the Erlang code faster than the baseline C, then someone else made the C version 8k % faster, which proves your point. However, how is that related to using sync/async vs message passing? If you want to write high-performance code then you need to be able to write synchronous code and have control over what your yield points are. If you take the "all calls are potentially async, runtime does what it wants" approach (i.e. "no function colouring") then you just will not be able to do that.
- igouy 2y ago> … Erlang … inspired by the message passing semantics of Smalltalk. What makes you think that?
- goatlover 2y agoThe Wikipedia entry says Smalltalk was one of it's influences and Joe Armstrong, co-developer of Erlang, mentions message passing as the fundamental aspect of OOP that Erlang gets right.
- rco8786 2y agoLegacy codebases that are a joy to work with are few and far between, in any language.
- segfaltnh 2y agoRails doesn't scale well in my experience. Or maybe rails devs don't scale well. The language and framework are both centered around developer happiness, which in my experience drops off around 10,000 lines. That's about when projects start getting difficult.
- pmontra 2y agoA Rails project I'm working on has this LOC Ruby 12169 ERB 2339 Vue 24895 Js 4526 The Vue frontend is indeed more complex than the Rails backend, and in my experience Vue is much simpler than React. My customer organized the Rails app with models, controllers, api/v1/controllers, jobs, services (naming only the most important stuff). It's not bad to work with.
- norman784 2y agoI also work on a legacy Ruby/Rails codebase, what I dislike is Ruby as a dynamic language, I'd prefer typed languages, but overall Rails didn't changed too much in the last 14 years that I know it (I didn't used too much in the past), but the concept is still the same to this day, few changes to the API/syntax, but otherwise if you know Rails, if you know Rails, it is most likely that you find very easy to work on any Rails app.
- throw10920 2y ago> Knowing ahead of time which functions are async is a feature. "Expensive" is subjective and should be up to the programmer to decide. And, it absolutely should not require creating two identical function definitions to get around the function coloring problem - that's completely indefensible. > This is a good thing for programmers to easily see and know at the call site. Yes, and the IDE can show you that information, exactly like it does the types of arguments. We don't redundantly write the types of arguments at the call site because it's a good thing for programmers to see (which it is), we let the IDE do that.
- mattigames 2y agoWhy have Json? We can always use binary to save a lot of space and bandwidth and the IDE will always compile it into plain text for us, why have underscore at the beginning of private variables? We can let the IDE do that, we even have long named functions instead of single letter ones? Most IDEs can read the jsdocs and give us a long description of what every function does, and I could go on and on. At the end it's a balance and subjective preferences of what you want to be immediately visible or something a bit more obscure (e.g. when you move your mouse over a variable)
- throw10920 2y ago> Why have Json? We can always use binary to save a lot of space and bandwidth and the IDE will always compile it into plain text for us Yes, this is a great idea, and people would do it if there was widespread support for a binary structured format. The difference between this incredibly ignorant sarcasm and my point is that IDEs already support the thing that I'm suggesting. You can already hover over a method and it'll give you information about the types of the arguments and return values that are specific to the call site. > At the end it's a balance and subjective preferences ...and you completely missed the main point of my comment, which was not about what information to show, but avoiding duplication due to function coloring: > And, it absolutely should not require creating two identical function definitions to get around the function coloring problem
- CGamesPlay 2y ago
- lta 2y ago> is a feature I was quite shocked to read this, as in it never brushed my mind that it could be. I personally doesn't feel like it is and is one of the reasons why I try to avoid working with that stack at all cost. I don't think the neon sign is a good excuse for the mess colored functions are. You easily can create a synchronous O(n^4) function, and there are probably tons of quick async functions. Moreover, your comment might be read as a free pass for using functions and methods that you're not really understanding the behavior off, it obviously doesn't sound like a good thing The information is only hidden with colorless methods if you consider the documentation to be a place to hide information (o^o).
- slibhb 2y agoAsync/await is clearly a feature in JS, though not for the reason the previous poster mentions. Async/await wasn't part of the language until 2017. If you don't like it, you can just use Promises. If you do that, there are no "colored function" (functions of color?). As far as the article goes, I think the pattern in Ruby is great. I prefer it to JS. But the JS approach works fine and the whole controversy about colored functions is a little silly. I would be interested in understanding why JS can't enable await from inside non-async functions. I (maybe naively) wonder if the compiler couldn't just figure out which functions to treat as async rather than making the programmer do it.
- spankalee 2y agoUsing promises is basically no different than using async functions, and definitely doesn't "uncolor" your functions. Any function that calls another that returns a Promise will have to return a Promise to represent completion correctly. That's the color.
- dragonwriter 2y ago> Any function that calls another that returns a Promise will have to return a Promise to represent completion correctly No, it doesn't, closures let you update values in the wcope of the parent in .then() callbacks and then you can loop in the function and wait for completion and return a non-Promise value.
- akoboldfrying 2y agoIt's a big neon sign that says "Either this function is expensive, or it isn't but some framework somewhere that sometimes needs to call expensive functions might also need to call this function using the same syntax".
- resonious 2y agoThis feels very true in Rust but not sure about JS. In JS you can `await` non-promise values without issue.
- lolinder 2y agoNo, because in JavaScript/TypeScript you can happily await any function that you'd like on the chance that it sometimes returns a Promise. If a framework demands that you return a Promise unnecessarily, that's on the framework, not the language.
- zogrodea 2y agoI had the same opinion with async/await, that it's nice to know a function performs IO and will wait before continuing. Makes it clearer when to use Promise.all to make multiple requests in parallel and wait for all of them to finish before continuing (faster than making calls sequentially). I kind of wish the languages I use had Haskell's IO monad too, to separate functions in terms of the type system, but that's slightly different. You might like this article (which is my personal favourite about function colouring). https://www.tedinski.com/2018/11/13/function-coloring.html https://www.tedinski.com/2018/11/13/function-coloring.html
- grumple 2y agoI hate that async functions are the ones we are forcing to wait and become synchronous. The naming was a failure imo.
- selfmodruntime 2y agoNot really. What it really means is that some function in this callstack is expensive. Async Await bubbles to the top of your program. Eventually, you might just read this as this application as a whole might be expensive.
- icholy 2y ago> Also, a nitpick - you can call async functions from sync ones, you just can't access the return value. Or wait for them to finish.
- dragonwriter 2y ago> Knowing ahead of time which functions are async is a feature. > It's a big neon sign that says "hey, this function call is expensive" Async and expensive aren't the same thing. > Also, a nitpick - you can call async functions from sync ones, you just can't access the return value. That's a quirk of the particular async/await implementation in JS and not generally true of colored-function implementations. (Actually, since async/await is sugar for promises, you can actually, I’m pretty sure, both call and use syntactically async functions from ones which are syntactically not-async if you really want to, the code is just ugly.
- klysm 2y agonit: I don't think _expensive_ is quite the right way to think about it. I view it as "hey, this function does IO". The actual cost of doing that IO varies immensely.
- munificent 2y ago> Knowing ahead of time which functions are async is a feature. It can be, but this fact doesn't always scale well to larger APIs over time. If you see that a function is async, any of these could be true: 1. The function does some asynchronous work. 2. The function did some asynchronous work at some point in the past, but does no longer, but changing the signature is a breaking API change, so it still looks async. 3. The function does no asynchronous work, but it's a public API and the library maintainer thinks there is a good chance it may need to do async work in the future so made it async today to reserve the right to make that change without breaking the API. 4. The function may or may not do asynchronous work, but it's part of an interface that others are allowed to implement and the designer of the interface wants to give implementers the freedom to make their implementations async if they want. 5. The function is higher-order and wants to be able to accept callbacks that are themselves async. There are probably others I'm forgetting. This all sounds hypothetical but it's really not. If you maintain any widely-used long-lived package, you run into design questions like this all the time. There is always a tension between an API consumer wanting to know what a function "really" does versus the API author wanting some abstraction so that implementation details can be changed over time without breaking users. Asynchrony is one kind of effect or observable behavior that a user/maintainer of an API may or may not want to encapsulate, but it's not the only one. The C++ standard library documents and commits to the algorithmic complexity of most functions. Haskell function signatures pin down whether or not a function does IO. Statically typed languages pin down the types parameters must be. Pinning down more of this stuff gives the API consumer some more information, but it's not always a pure win. It calcifies the behavior of the function in ways that can harm its evolution or interact poorly with higher-order code or polymorphism. There's no silver bullet. Personally, I've never found "does this function suspend" to be a particularly interesting effect for a function to have to commit to and the fact that it poisons the entire callstack makes it very difficult to work with in practice.
- phplovesong 2y agoIts a bad feature. The thing is it does not matter if its ”obvious” as anything that touches async needs to be async too. Its a bad paradigm, CSP is obviously a better way to do concurrency. As async is usually only IO bound, but how about CPU bound? In the node ecosystem CPU bound tasks are not something you do with async/await.
- vlucas 2y agoIn node, you generally don't want to do anything CPU bound.
- lolinder 2y agoYep. If you have CPU-bound work then you should use a different language. If you have IO-bound work then explicitly tagged async functions are actually a really nice concurrency model for the only kind of concurrency you need—it's equivalent to having the Haskell IO monad as a first-class language feature.
- caditinpiscinam 2y agoIf explicitly marking expensive IO operations using async is a feature, then why don't languages with async make their "print" functions asynchronous?
- unscaled 2y agoWhat about a language which has both lightweight threads, but in which every I/O operation (including printing and getting a random number) is colored? https://www.haskell.org/tutorial/io.html https://www.haskell.org/tutorial/io.html