Live data from Hacker News

Ruby methods are colorless

jpcamara.com

221–230 of 242 posts

Re: Ruby methods are colorless

#221
post #114

Earlier quoted context omitted.

> The information is only hidden with colorless methods if you consider the documentation to be a place to hide information (o^o). No, source code is where you hide the information, documentation is where you hide misinformation (or accurate information about how the function worked 3 versions ago, which is much the same thing).

The code tells you what the code _does_. The documentation (which can include comments in the code) tell you what the code is intended to do. Either one of those can be wrong. If the code doesn't match the documentation, then there is a bug in the system. Not having documentation just removes the ability to determine if what the code _does_ is what the code is _intended to do_.

> If the code doesn't match the documentation, then there is a bug in the system.

Then I'd say the vast majority of Ruby libraries are buggy.

> Not having documentation just removes the ability to determine if what the code _does_ is what the code is _intended to do_.

But usually there isn't any intent when it comes to async-or-not. Library authors usually just write a method that results in the right value; whether that method can yield or not isn't even something they thought about, much less had a specific design in mind for.

Re: Ruby methods are colorless

#222
post #214

Earlier quoted context omitted.

Can you give an example of a language which is polymorphic in this way and how that looks?

Haskell or Scala are the immediate examples. The idiomatic way to write async-style functions in those languages tends to be do notation or for/yield style. E.g. transformFile = do { x is the equivalent of something like: async transformFile = { x = await readFile(...) y = process(x) z = await writeFile(...) } In Haskell you write exactly the same code when readFile and writeFile are async, or when they're polymorphi…

Ah I've got it. Async is a monad and the function is just polymorphic over Monad m. In the sync case m is Identity. Can you point me in the direction of some libraries which offer functions like these?

Re: Ruby methods are colorless

#223

Earlier quoted context omitted.

If you program with promises and without async/await, then your language is still missing something compared to languages that have coroutines in which functions that suspend and do not suspend may be composed (used by higher-order functions, etc.) in the same ways as each other. You've moved from the situation where both types of functions were present and must be handled differently to the situation where one type…

Can you give an example?

An example of a language or of a program? You could try Lua, Python with greenlet, Zig 0.10.0 or Zig master with zigcoro, dozens of libraries that add this sort of capability to C, or becoming a the kind of person who uses search engines when they have questions.

BulletML is not even Turing complete and still has a wait function that does the exact thing mentioned.

Re: Ruby methods are colorless

#224
post #203

Earlier quoted context omitted.

runBlocking {}

Right that solves the problem if you can block. But in applications that are async everywhere, like a web app, you end up having to mark everything as suspend all the way up the chain.

Which is fine?

Re: Ruby methods are colorless

#225

Earlier quoted context omitted.

This is not just about performance. Unlike Go or Ruby, JavaScript is a single-threaded language. Synchronization constructs such as mutexes and semaphores are uncommon and not part of any standard library. When you are calling a synchronous function, you can be completely assured that no race condition can develop while the function is running, but the same guarantee is not true for asynchronous functions. That's why…

"Now you have two problems." Adding async/await was a hack to avoid having to tackle the hard problem of real concurrency, so now you have a) no real concurrency, and b) coloured functions. There is a parallel universe where JS added almost any other concurrency primitive and got a better trade-off than async/await.

There is a parallel universe in which Brendan Eich was given more than 10 days to hack on JavaScript, and in which he presented the language with full-blown stackful coroutines instead of hacky callbacks for concurrency. There might be even a parallel universe in which '[] + 0' produces an error rather than the string "0".

Unfortunately, we do not live in that parallel universe, and JavaScript had to evolve the way it did. When callbacks have already been established as the standard concurrency mechanism, promises were a big improvement. When promises became standard, async/await was the natural evolution rather than breaking existing programs by introducing stackful coroutines.

Other languages which chose async/await (Rust, C#, Kotlin) had their own reasons. In all of the cases above, the language designers wanted to give the user more control over the scheduler (Colorless Stackful coroutines require a global scheduler).

In Rust's case, it's async/await state machines are just not wasting memory and unnecessarily copying it like stackful coroutines do (not to mention that Rust doesn't have a garbage collector and pointer rewrites, so it cannot just copy stacks around when they grow). In fact, Rust did have garbage collection and green threads, but they were both removed early on, as the language changed its focus to became a language where you can expect performance that are _at least_ on par with C or C++.

In case of Kotlin, this all mostly had to do with finding an abstraction that can run on pre-Loom JVMs, JavaScript targets and native targets. Kotlin does not own the runtime.

Saying everybody should follow Go here is silly. Not all languages share the same background and design constraints. Go-style Stackful coroutines require garbage collection, full control of the runtime and generally a language that is designed from scratch to have them - or a 6-year project to adapt your language and runtime to have them, like Project project Loom did.

Async/await imposes some costs, but the benefits (compatibility, performance, flexibility) often outweigh these. After working with both async functions and stackful coroutines in multiple languages, I cannot say I ever felt concurrency in Go to be more ergonomic than Kotlin. If anything, it was the reverse for me in that particular case (owing to Go's lack of reification for goroutines and verbose syntax). Deciding in advance what color is my function (i.e. whether it needs to deal with I/O), is pretty simple and it rarely changes during the lifetime of a program (and even when it does, refactoring is not hard unless your design is bad).

The only case where I did feel some pain using async/await, was Rust, and this has more to do with Pin, competing async runtimes (for a while), lack of support for async traits (for a while) and other areas where Async Rust still needs some work.

Re: Ruby methods are colorless

#226
post #81

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…

If explicitly marking expensive IO operations using async is a feature, then why don't languages with async make their "print" functions asynchronous?

What 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

Re: Ruby methods are colorless

#227
post #214

Earlier quoted context omitted.

Haskell or Scala are the immediate examples. The idiomatic way to write async-style functions in those languages tends to be do notation or for/yield style. E.g. transformFile = do { x is the equivalent of something like: async transformFile = { x = await readFile(...) y = process(x) z = await writeFile(...) } In Haskell you write exactly the same code when readFile and writeFile are async, or when they're polymorphi…

Ah I've got it. Async is a monad and the function is just polymorphic over Monad m. In the sync case m is Identity. Can you point me in the direction of some libraries which offer functions like these?

sttp (HTTP client), http4s (HTTP client/server), quill (database access), fs2 (streaming data processing), I have some vague memory of a gRPC implementation. A lot of stuff that does async in Scala tends to be written in this style because it's almost no extra cost compared to writing it in strictly async fashion.

Re: Ruby methods are colorless

#228
post #162

Earlier quoted context omitted.

Yes, it does, and Go is perfectly capable of it, and many libraries exist for you to choose which exact method suits your problem and temperment. One of the common pasttimes in the threaded versus async debate is to present code in which one side uses all sorts of helpers and patterns and libraries and the other side is presented through writing it "raw". The great-grandparent of my post here is guilty of this. While…

No… I fear you’ve missed the entire point of the matter, which is that async/await requires that you must go all the way up the call stack explicitly “await”ing things when you have introduced an “async” call (or similar wide spread changes to that effect). There’s no special magic utility function you can call you hide it away. That’s the whole point – and a very good thing, this thread argues.

No, it is perfectly feasible to abstract around it. It's just that the abstractions are also colored. But there is no more a rule that you can only "await" a promise right in the exact code where you created the promise than there is that the only way to use threads is to spawn them right on the spot and then wait for the result right on the spot. Critics of both async and threads are just dead wrong on this, and observably, objectively so, since libraries in both cases not only exist, but are readily available and abundant.

And I'm admitting this "against interest", as the lawyers say. I'm not striking a disinterested "middle of the road" pose here. I'm hugely on the side of threads. But it is still not a relevant criticism of async. You can easily "parallel map" with either abstraction and you are not stuck unable to abstract the control flow in either case.

Re: Ruby methods are colorless

#229
post #164

Earlier quoted context omitted.

import github.com/carlmjohnson/flowmatic func whatever(ctx context.Context) { err := flowmatic.Race(ctx, taskA, taskB) } Provide your definition of taskA and taskB, of course. As I said in another message, this is not a particularly fruitful line of attack in either direction. All the languages in question are perfectly capable of abstractions.

I don't like that the return values of the tasks has to be communicated with side effects, but I'll concede that it's quite painless. I guess I'm just still salty when someone commented (in another post a long time ago) that golang only has `go` (compared to `launch`, `async`, and `coroutineScope` in Kotlin) and is simpler. > All the languages in question are perfectly capable of abstractions. I don't think async fun…

"I don't like that the return values of the tasks has to be communicated with side effects, but I'll concede that it's quite painless."

Me neither, however, it is generally the most flexible approach and I can see why a library takes it. If you want to communicate it via the return, you also have to impose a restriction that the tasks all return the same type. I think it makes sense for a library to work this way because you can easily add this around the library call yourself, but it's more difficult to go the other way. (Not impossible, just more difficult.)

"I don't think async functions in JS can be cancelled though."

Poking around, it looks pretty hard.

That said, cancelling in generally is very difficult in imperative languages, so even as someone who finds async in JS quite distasteful I can't dock too many points. Go basically just reifies the JS solution into a standard community practice, which is definitely an improvement since you can largely rely on it being supported everywhere, but one could reasonably debate how good it is. It is occasionally a problem that if you want to cancel an ongoing computation you may have to have your code manually check a context every so often, because there's no "real" connection between a context and a goroutine.

Re: Ruby methods are colorless

#230
post #228

Earlier quoted context omitted.

No… I fear you’ve missed the entire point of the matter, which is that async/await requires that you must go all the way up the call stack explicitly “await”ing things when you have introduced an “async” call (or similar wide spread changes to that effect). There’s no special magic utility function you can call you hide it away. That’s the whole point – and a very good thing, this thread argues.

No, it is perfectly feasible to abstract around it. It's just that the abstractions are also colored. But there is no more a rule that you can only "await" a promise right in the exact code where you created the promise than there is that the only way to use threads is to spawn them right on the spot and then wait for the result right on the spot. Critics of both async and threads are just dead wrong on this, and obs…

Show me a JS library that allows you to swap a non-async call for an async one in a non-async context^ and I’ll eat my hat.

^Without any non-local changes, obviously.

Post reply on HN