Remix.run Logo
warmwaffles 9 hours ago

Async usually ends up being a coloring function that knows no bounds once it is used.

amonroe805-2 9 hours ago | parent [-]

I’ve never really understood the issue with this. I find it quite useful to know what functions may do something async vs which ones are guaranteed to run without stopping.

In my current job, I mostly write (non-async) python, and I find it to be a performance footgun that you cannot trivially tell when a method call will trigger I/O, which makes it incredibly easy for our devs to end up with N+1-style queries without realizing it.

With async/await, devs are always forced into awareness of where these operations do and don’t occur, and are much more likely to manage them effectively.

FWIW: The zig approach also seems great here, as the explicit Io function argument seems likely to force a similar acknowledgement from the developer. And without introducing new syntax at that! Am excited to see how well it works in practice.

newpavlov 8 hours ago | parent | next [-]

In my (Rust-colored) opinion, the async keyword has two main problems:

1) It tracks code property which is usually omitted in sync code (i.e. most languages do not mark functions with "does IO"). Why IO is more important than "may panic", "uses bounded stack", "may perform allocations", etc.?

2) It implements an ad-hoc problem-specific effect system with various warts. And working around those warts requires re-implementation of half of the language.

echelon 8 hours ago | parent [-]

> Why IO is more important than "may panic", "uses bounded stack", "may perform allocations", etc.?

Rust could use these markers as well.

newpavlov 8 hours ago | parent [-]

I agree. But it should be done with a proper effect system, not a pile of ad hoc hacks built on abuse of the type system.

echelon 6 hours ago | parent [-]

`async` is in the type system. In your mind, how would you mark and bubble up panicky functions, etc.? What would that look like?

I felt like a `panic` label for functions would be nice, but if we start stacking labels it becomes cumbersome:

  pub async panic alloc fn foo() {}
That feels dense.

I think ideally it would be something readers could spot at first glance, not something inferred.

newpavlov 3 hours ago | parent [-]

>`async` is in the type system.

No, it's not. `async` is just syntax sugar, the "effect" gets emulated in the type system using `Future`. This is one of the reasons why the `async` system feels so foreign and requires so many language changes to make it remotely usable. `const` is much closer to a "true" effect (well, to be precise it's an anti-effect, but it's not important right now).

Also, I think it's useful to distinguish between effect and type systems, instead of lumping them into just "type system". The former applies to code and the latter to data.

>That feels dense.

Yes. But why `async` is more important than `alloc`? For some applications it's as important to know about potential allocations, as for other applications to know about whether code potentially yields or not.

Explicitly listing all effects would the most straightforward approach, but I think a more practical approach would be to have a list of "default effects", which can be overwritten on the crate (or maybe even module) level. And on the function level you will be able to opt-in or opt-out from effects if needed.

>I think ideally it would be something readers could spot at first glance

Well, you either can have "dense" or explicit "at first glance" signatures.

ecshafer 8 hours ago | parent | prev | next [-]

Is this Django? I could maybe see that argument there. Some frameworks and ORMs can muddy that distinction. But most the code ive written its really clear if something will lead to io or not.

warmwaffles 6 hours ago | parent | prev [-]

I've watched many changes over time where the non async function uses an async call, then the function eventually becomes marked as async. Once majority of functions get marked as async, what was the point of that boilerplate?