Live data from Hacker News

Ruby methods are colorless

jpcamara.com

21–30 of 242 posts

Re: Ruby methods are colorless

#21
> Async code bubbles all the way to the top. If you want to use await, then you have to mark your function as async. Then if someone else calling your function wants to use await, they also have to mark themselves as async, on and on until the root of the call chain. If at any point you don’t then you have to use the async result (in JavaScript’s case a Promise).

I find many descriptions of async code to be confusing, and this kind of description is exactly why.

This description is backwards. You don't choose to use await and then decorate functions with async. Or maybe you do and that's why so many async codebases are a mess.

You don't want to block while a long running operation completes, so you decorate the function that performs that operation with async and return a Promise.

But Promises have exactly the same value as promises in the real world: none until they are fulfilled. You can't do further operations on a promise, you can only wait for it to be done, you have to wait for the promise to be fulfilled to get the result that you actually want to operate on.

The folly of relying on a promise is embodied in the character Whimpy from Popeye: "I'll gladly pay you Tuesday for a hamburger today".

Once you have a promise, you have to await on it, turning the async operation into a synchronous operation.

This example seems crazy to me:

    async function readFile(): Promise {
      return await read();
    }
This wraps what should be an async operation that returns a promise (read) in an expression that blocks (await read()) inside a function that returns a promise so you didn't need to block on it!. This is a useless wrapper. This kind of construct is probably the significant contribution to the mess: just peppering code with async and await and wrapper functions.

await is the point where an async operation is blocked on to get back into a synchronous flow. Creating promises means you ultimately need to block in a synchronous function to give the single threaded runtime a chance to make progress on all the promises. Done properly, this happens by the event loop. But setting that up requires the actual operation of all your code to be async and thus callback hell and the verbose syntactic salt to even express that in code.

That all being said, this piece is spot on. Threads (in general, but in ruby as the topic of this piece) and go's goroutines encapsulate all this by abstracting over the state management of different threads of execution via stacks. Remove the stacks and async programming requires you to manage that state yourself. Async programming removes a very useful abstraction.

Independent threads of execution, if they are operating system managed threads, operating system managed processes (a special case of OS managed threads), green threads, or go routines, are a scheduler abstraction. Async programming forces you to manage that scheduling. Which may be required if you don't also have an abstraction available for preemption, but async leaks the single threaded implementation into your code, and the syntactic salt necessary to express it.

Re: Ruby methods are colorless

#22
As they should be.

I object to doing what a computer can do for me (in programming), and manually creating separate versions of functions that are identical up to async absolutely falls into that category.

Re: Ruby methods are colorless

#25
post #15

I've implemented coroutines in C and C++; my preferred multitasking environment is message-passing between processes. I'm not quite sure what the async/await stuff is buying us (I'm thinking C++, here). Like, I get multi-shot stackless coroutines, i.e., function objects, but I don't get why you'd want to orchestrate some sort of temporal Turing pit of async functions bleeding across your code base. I dunno. Maybe I'm…

My theory is that JavaScript programmers who were forced into thinking this way for decades with their single-threaded runtime have infected other languages with the idea that this style of coding is not only good but furthermore that it needs to be explicit. Thank goodness we have wiser language developers out there who have resisted this impulse.

Didn’t async/await originate in c#?

Re: Ruby methods are colorless

#26

I don't like colored function for obvious reasons, but fully colorless for async means you don't know when things are async or not. There are a lot of things I dislike in JS, but I think the I/O async model is just right from an ergonomics point of view. The event loop is implicit, any async function returns a promise, you can deal with promises from inside sync code without much trouble. It's just the right balance.

> fully colorless for async means you don't know when things are async or not The IDE can tell you.

Given Ruby culture of monkey patching, not always.

Besides, many people dev Ruby with a lightweight text editor, like text mate, that can't introspect code.

Re: Ruby methods are colorless

#27
post #15

I've implemented coroutines in C and C++; my preferred multitasking environment is message-passing between processes. I'm not quite sure what the async/await stuff is buying us (I'm thinking C++, here). Like, I get multi-shot stackless coroutines, i.e., function objects, but I don't get why you'd want to orchestrate some sort of temporal Turing pit of async functions bleeding across your code base. I dunno. Maybe I'm…

Coming from a heavy TS background into a go-forward company, I’d say the main thing you get with async is it makes it incredibly obvious when computation can be performed non-sequentially (async…). For example, It’s very common to see the below in go code:

   a := &blah{}
   rA, err := engine.doSomethingWithA()
   b := &bloop{}
   rB, err := engine.doSomethingWithB()
This might have started out with both the doSomethings being very quick painless procedures. But over time they’ve grown into behemoth network requests and very thing is slow and crappy. No, it’s not exactly hard to spin up a go routine to handle the work concurrently, but it’s not trivial either - and importantly, it’s not immediately obvious that this would be a good idea.

Contrast to TS:

   let a = {blah}
   let [rA, err] = engine.doSomethingWithA()
   let b = {bloop}
   let [rB, err] engine.doSomethingWithB()
Now, time passes, you perform that behemoth slowing down of the doSomethings. You are forced by the type system to change this:

   let a = {blah}
   let [rA, err] = await engine.doSomethingWithA()
   let b = {bloop}
   let [rB, err] await engine.doSomethingWithB()
It’s now immediately obvious that you might want to run these two procedures concurrently. Obviously you will need to check the engine code, but any programmer worth their salt should at least seek to investigate concurrency when making that change.

I wouldn’t be bringing this up if I hadn’t made 10x+ performance improvements to critical services within a month of starting at a new company in a new language on a service where the handful of experienced go programmers on the team had no idea why their code was taking so long.

Re: Ruby methods are colorless

#28
post #25

Earlier quoted context omitted.

My theory is that JavaScript programmers who were forced into thinking this way for decades with their single-threaded runtime have infected other languages with the idea that this style of coding is not only good but furthermore that it needs to be explicit. Thank goodness we have wiser language developers out there who have resisted this impulse.

Didn’t async/await originate in c#?

F# first actually. Then C#. Then Haskell. Then Python. Then TypeScript. Parent just has an axe to grind.

Re: Ruby methods are colorless

#29
Anytime this comes up I plug the excellent "Unyielding" (https://glyph.twistedmatrix.com/2014/02/unyielding.html) and "Notes on structured concurrency" (https://vorpus.org/blog/notes-on-structured-concurrency-or-g...) as the counterpoint to "What color is your function". Being able to see structurally what effects your invocation of a function will result in is very helpful in reasoning about the effects of concurrency in a complicated domain.

Re: Ruby methods are colorless

#30

> Async code bubbles all the way to the top. If you want to use await, then you have to mark your function as async. Then if someone else calling your function wants to use await, they also have to mark themselves as async, on and on until the root of the call chain. If at any point you don’t then you have to use the async result (in JavaScript’s case a Promise ). I find many descriptions of async code to be confusin…

To be fair to the author, they do mention in the paragraph above that sample:

    Async code bubbles all the way to the top. If you want to use await, then you have to mark your function as async. [...] If at any point you don’t then you have to use the async result (in JavaScript’s case a Promise).
I think it's just an artificially lengthy example to show how the responsibility of working with promises grows up the callstack. Interpreting it that way since the final function they define in that sample is `iGiveUp` which is not using the async keyword, but returns a promise. Definitely could be made a bit more clear that's it's illustrative and not that the async keyword is somehow unlocking some super special runtime mode separate from it's Promise implementation.
Post reply on HN