5 ms·
> Even though Rust does slightly skirt the colored function issue [...] knowing when something you call blocks, or grabs a lock, etc. is something which the com
by jakear 4y ago
> Even though Rust does slightly skirt the colored function issue [...] knowing when something you call blocks, or grabs a lock, etc. is something which the compiler doesn’t help you with but should.
Are not colored functions precisely the compiler helping you identify when the code has the potential to block? If I write a block of JS in a non-async function context, I can be 100% guaranteed that no other task will interfere with my function's execution. If I write a block of JS in an async context, I can be 100% guaranteed that the only times when any other task may interfere with my functions execution are explicitly marked with `await`. I love this! I can't imagine writing serious code without it (barring some go-like messaging model), and I have no idea why people hate it so.
- dcow 4y agoI think you misunderstand my point slightly. Here’s the difference: in JS everything that needs to be async is actually marked async. So when you use a browser api or library function you know for certain. I agree I like that and want it for Rust. In Rust the standard lib is not async aware so it cant be used safely from async code, period. And tons of Rust code uses the stdlib and it’s not super practical to just not use it so you’re stuck asking whether something is safe to call from async code or not with zero help from the compiler. So we agree not having the compiler do this sucks. (What I meant by partially skirting the colored function issue is that you don't have to call await on the result of a function if you dint want to. So you can call a blue function from a red function just fine and do stuff with the result. You just can’t await it. This adds back lots of flexibility that some async impls in other languages don’t allow for.)
- jakear 4y ago> you can call a blue function from a red function just fine and do stuff with the result. You just can’t await it By this do you mean you can choose between waiting for a value in a way that halts the entire rest of the runtime while it resolves and one that will attempt to perform other tasks while waiting for the resolution?
- dcow 4y agoEssentially yes. Any time you call an async function you get a Future. The future does not resolve until you await it. So you can call an async function from a non-async function normally and do things with the future. You just cant call future.await to yeild until the result is is ready unless you’re in an async context. You can ask the runtime to resolve the future if you want, but that’s a blocking call so it’s up to you to make sure it’s safe to block.
- jakear 4y agoOh, well that's exactly the same as JS. You can await it to get a value but you don't have to, you could instead fire-and-forget, batch it up over time and await them all later, or attach a function to run with the resolved value whenever it resolves while letting the current execution move along unhampered. If you don't want to `await` it, your function stays "red" or whatever.
- ithkuil 4y agoThe main difference between rust futures and JS promises is that rust futures don't get scheduled if you don't poll them (we high await does under the hood). Promises otoh get to run immediately and can run to completion even if you never touch the promise ever againt
- tijsvd 4y agoYou get the same with e.g. tokio::spawn, which runs the future concurrently and returns something that you can await and get the future's output. Or you can forget that something and the future will still run to completion. Directly awaiting a future gives you more control, in a sense, as you can defer things until they're actually needed.
- mike_hearn 4y agoSome downsides. The term blocking isn't well defined. How slow does an operation have to be to be considered blocking? There are no rules. Fetching google.com/robots.txt is by universal agreement a blocking operation. Reading a file from the filesystem is considered blocking in JS but may not be in other languages, especially given that very fast disks like Optane or modern NVMe Flash can be only an order of magnitude or two slower than RAM. Taking a lock may or may not be considered blocking. Computing pi to a trillion digits would usually not be considered blocking because it doesn't make any async OS API calls, even though it might cause long delays before it returns, but if the author chose to implement it as an async iterator then it could be considered blocking. Worse, async vs not async is about latency requirements of the caller, but the function author can't know that. If you're in a UI thread doing 60fps animation maybe even reading a file would be too slow, but if you're in a script then it doesn't matter at all and you'd rather have the simpler code. You're calling the same function, the only thing that changed is the performance requirements of the user. So "potential to block" is to some extent a makework problem that occurs in JS because V8 isn't thread safe, and V8 isn't thread safe because JS isn't, because it was designed for the web which is a kind of UI toolkit. Such toolkits are almost never thread safe because it's too much hassle to program a thread safe renderer and would make it too slow, so that browser-specific implementation constraint propagates all over the JS ecosystem. Final major problem: some languages use async hype as an excuse to not properly support threads at all, but that's often the only way to fully exploit the capabilities of multicore hardware. If your language only does async you'll always be restricted to a single core and you have to hope your use case parallelizes embarrassingly well at the top level. If you have threads then this whole problem just goes away. That's the core argument that Java makes with Project Loom. Instead of introducing colored functions everywhere with poorly defined and inconsistent rules over which color to use, just make threads work better. Suddenly the binary async/not async distinction becomes irrelevant. Still, there's a good reason most languages don't do the Loom approach. It's a massive PITA to implement. The Java guys are doing it because of their heavy investment in advanced VMs, and because the Java ecosystem only rarely calls out to native code. It'd be a lot harder in an ecosystem that had less of a focus on "purity".
- jakear 4y ago> The term blocking isn't well defined. How slow does an operation have to be to be considered blocking? There are no rules. The rule is very simple IMO, I already described it: if other execution can run while you wait for the value, it's blocking. (Edit: to be specific, other observable execution, so in your thread with write access to state you can see) > Worse, async vs not async is about latency requirements of the caller, I disagree. To me, async vs not async is about interruptibility. As you already mentioned, the code that computes a trillion digits of Pi is async if its interruptible, and not async if it isn't. If you're in a UI thread doing 60fps animation, async vs not async isn't really all that relevant (barring the slight inherent overhead in calling an async function). What you need is a profiler to tell you where you have a non-interruptible block consuming your 16ms. The compiler can't know this a priori, unless you make significant restrictions to the type of code you write (no unbounded loops, for instance). > Such toolkits are almost never thread safe because it's too much hassle to program a thread safe renderer and would make it too slow, so that browser-specific implementation constraint propagates all over the JS ecosystem. From here on you seem to not be aware of the fantastic recent developments in WebWorkers, it is now quite easy to spin up worker threads in JS, which communicate with the main thread via message passing.
- adql 4y agoThe point is that having to think about color of the function is disadvantage and needless complexity > If I write a block of JS in an async context, I can be 100% guaranteed that the only times when any other task may interfere with my functions execution are explicitly marked with `await`. I love this! You only need to think about it because runtime isn't parallel. If you want ordering in execution you should be using synchronization primitives anyway. In Go doing "async" via channels could possibly start running the code the instant you instantiate it, on however cores there are available. Go have problem of that requiring a bunch of boilerplate (as there is no macros), but with generics now it is manageable and I can just say "here is function, run it on all elements of the slice in parallel" very easily and it all be pretty reasonabe, like "out = ParallelMapSlice(mapFunc,8,in)". All done async without thinking about async.
- adql 4y agoThe point is that having to think about color of the function is disadvantage and needless complexity > If I write a block of JS in an async context, I can be 100% guaranteed that the only times when any other task may interfere with my functions execution are explicitly marked with `await`. I love this! You only need to think about it because runtime isn't parallel. If you want ordering in execution you should be using synchronization primitives anyway. In Go doing "async" via channels could possibly start running the code the instant you instantiate it, on however cores there are available. Go have problem of that requiring a bunch of boilerplate (as there is no macros), but with generics now it is manageable and I can just say "here is function, run it on all elements of the slice in parallel" very easily and it all be pretty reasonabe, like out = ParallelMapSlice(mapFunc,8,in) All done async without thinking about async.
- jakear 4y agoIf mapFunc reads state it also writes, you should be thinking about async. If it doesn't, the problem isn't all that interesting. Though I did admit in the parent comment that Go's approach is reasonable. It should be noted that similar code is possible in JS with WebWorkers. Async/Await is more for same-thread concurrency.