11 ms·
This remind me of the blog post "What Color is Your Function?"[0], they had to create a different library that is the same as the standard library but with asyn
by Un1corn 7y ago
This remind me of the blog post "What Color is Your Function?"[0], they had to create a different library that is the same as the standard library but with async functions.
I thought Rust had other, better ways to create non-blocking code so I don't understand why to use async instead.
[0] https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...
- pohl 7y agoThe caller of the function knows nothing about what happens within the body of the function. (Is it just doing computation, or is it doing I/O?). The async keyword is how the author of the function makes it explicit that caller should choose when to await the result. Isn't the alternative WCiYF is proposing to allow the caller to treat any function asynchronously, while having no way to discern whether doing so might be counterproductive?
- Argorak 7y agoRust async functions are also different here. They don't run the code, they create a Future structure in it's initial state. Contrary to e.g. JavaScript, it doesn't start to run until you end up putting it on an executor. So the actual function call does something very different from what happens in other languages. It's sometimes called "cold futures".
- aphexairlines 7y agoSounds similar to what the Scala ecosystem calls Tasks. https://monix.io/docs/3x/eval/task.html#introduction https://monix.io/docs/3x/eval/task.html#introduction https://github.com/traneio/arrows#using-task https://github.com/traneio/arrows#using-task
- Argorak 7y agoIt's similar in some ways, yes. As always, details and labels differ.
- bryanlarsen 7y agoNone of the 5 points in that article about callbacks in 2015 node.js apply to async in Rust. The Rust people spent years agonizing over their version of async and applied a lot of lessons learned from implementations in other languages. https://news.ycombinator.com/item?id=20676641 https://news.ycombinator.com/item?id=20676641 It's trivial to turn async into sync in Rust. You can use ".poll", "executor::block_on", et cetera. Turning sync into async is harder in any language. Even Go with it's easy threading. That's a good argument to make async the default in libraries in Rust, but since async isn't stable yet, that would have been hard to do 5 years ago.
- merb 7y ago> Turning sync into async is harder in any language. well in most languages you can wrap sync into async. so it's not "hard". it's just harder to have NON blocking code. i.e. in c# there is a difference between: `await Task.Run(() => Thread.Sleep(5000));` and `await Task.Delay(5000);` both will wait for 5 seconds but one will waste cpu cycles while the other won't.
- gpm 7y agoIncluding in rust
- mrighele 7y ago> well in most languages you can wrap sync into async. so it's not "hard" It is not easy to do in a cirrect and performant way. "async" doesn't mean "code that runs in another thread". You can have a single threaded runtime running async code (that's usually the case for javascript). The "async-ness" is in those cases provided by the use of non-blocking primitives for IO, network etc. If a function is making a blocking call to the file system even if you make it async it will not help since the main thread will still be blocked on that system call. The performance will also be quite different: waiting for data on 10000 sockets in a non-blocking way is quite different from having 10000 threads doing the same.
- dnautics 7y ago> Turning sync into async is harder in any language. Elixir's Task module (in the stdlib): future = Task.async(fn -> do_something_here end) ...do_other_things... result = Task.await(future, timeout) Mixing it with the Enum library makes concurrency dead-simple (got I a junior dev dispatching concurrent tasks in scripts with confidence), at the expense of an ugly nested double lambda. some_list_of_values |> Enum.map(fn value -> Task(fn -> do_something_with(value) end) end) |> Enum.map(&Task.await(&1, timeout))
- Jayschwa 7y agoAs an aside, Zig[0] recently merged a change to try and tackle the use case of [a]sync-agnostic functions: https://github.com/ziglang/zig/issues/1778 https://github.com/ziglang/zig/issues/1778. The "What Color is Your Function" blog post appears to have been one of the inspirations behind the change. Some of Andy's (the language creator) recent videos go over it in detail: https://www.youtube.com/channel/UCUICU6mgcyGy61pojwuWyHA https://www.youtube.com/channel/UCUICU6mgcyGy61pojwuWyHA [0]: https://ziglang.org https://ziglang.org
- MrBuddyCasino 7y agoThat looks quite interesting! The idea is that you can set the global "io_mode" mode to blocking, mixed or evented and I/O functions will switch their implementation accordingly. The type of the function will then, if I got that right, propagate up the call stack and turn functions that touch it transparently into either normal or async/awaitable funtions. Nice way to avoid a bifurcation of the ecosystem into red/green functions. Its a bit magical maybe, any other trade-offs?
- runeks 7y agoPlease excuse my cynicism, but how can a global variable for switching between blocking and async be considered interesting? I mean, don’t you know at compile-time whether you want something to be async or not? If so, it should be handled by the type system, not by mutating a variable at runtime.
- MrBuddyCasino 7y agoI believe this is a compile time setting. The point is to avoid the red/blue function color issue, which has implications for all functions upstream of I/O functions. It literally is about the type system. Did you even read the linked text?
- gonzus 7y agoOne trade-off at the moment (if I understood this correctly) is that function pointers lose this transparency, and you have to be explicit about them being async (or not). Maybe the plan is to lift this restriction in the future? I am not privy to that.
- zzzcpan 7y agoThe point of asynchronous programming is to know exactly where concurrency happens in your code. This both eliminates concurrency bugs and gives you predictability for high performance.
- otabdeveloper2 7y agoCooperative multitasking, like it's 1995 again? No thanks.
- zzzcpan 7y agoAsynchronous programming is not cooperative multitasking.
- edflsafoiewq 7y agoHow is it not?
- hathawsh 7y agoThey are very similar. In cooperative multitasking, programs yield the thread to the OS, while async programs yield to the event loop. In both cases, yielding is voluntary. Are there other differences? It's probably quite easy to turn a program written one way into the other. Edit: I just remembered that in cooperative multitasking, it's probably possible for the OS to safely save the program stack pointer, meaning the program doesn't have to unwind its stack when yielding, unlike async programs. Never mind, that makes the two models quite different. However, in practice, programs written for cooperative multitasking really should be structured just like async programs in order to be responsive (so users can, for example, interact with the GUI while downloading files in the background.)
- zzzcpan 7y agoEven conceptually the models are very different, in one control is just given up and regained unpredictably, while in the other one it is programmed, hence asynchronous programming, not multitasking.
- lmm 7y agoThe right way to handle this stuff is polymorphism. But with Rust lacking higher-kinded types I guess that's not possible. A decent test of whether their "associated type constructors" actually solve the same problems, as sometimes claimed, would be whether you can write this kind of async-polymorphic code with them.
- pcwalton 7y ago> I thought Rust had other, better ways to create non-blocking code so I don't understand why to use async instead. In fact, Rust does have a great solution for nonblocking code: just use threads! Threads work great, they are very fast on Linux, and solutions such as goroutines are just implementations of threads in userland anyway. (The "what color is your function?" post fails to acknowledge that goroutines are just threads, which is one of my major issues with it.) People tell me that Rust services scale up to thousands of requests per second on Linux by just using 1:1 threads. Async is there for those who want better performance than what threads/goroutines/etc. can provide. If you don't want to deal with two "colors" of functions, you don't have to! Just use threads.
- temac 7y ago> The "what color is your function?" post fails to acknowledge that goroutines are just threads, which is one of my major issues with it. """Three more languages that don’t have this problem: Go, Lua, and Ruby. Any guess what they have in common? Threads. Or, more precisely: multiple independent callstacks that can be switched between. It isn’t strictly necessary for them to be operating system threads. Goroutines in Go, coroutines in Lua, and fibers in Ruby are perfectly adequate.""" What more do you need?
- nullwasamistake 7y agoThreads are bad for high concurrency. Specifically when you need to call out to another service that has some latency. Say you have 1000 threads. To handle a request each one needs to make 50ms of external or DB calls. In one second, each thread can handle 20 calls. So you can handle 20k requests/second with 1000 threads. But Rust is so fast it can serve 500k requests a second. So with regular threads, you need ~25,000 threads. The OS isn't going to like that. With async you can run a single thread per core, with no concurrency limits. So you get your 500k requests without overhead. With fibers you just run 20k fibers which is a little bit of overhead but easy to do. This is the core reason everyone is pushing async and fibers in fast languages. When you can push a ton of requests/second but each one has latency you can't control, regular threads will kneecap performance. In "slow" languages like Python, Ruby, etc, async/fibers don't really matter because you can't handle enough requests to saturate a huge thread pool anyways.
- dom96 7y agoThere are ways to get around this, the way that I have done it in Nim is via a `multisync` macro: proc readLine(s: Socket | AsyncSocket): Future[string] {.multisync.} = while true: let c = await s.recv(1) case c of '\n': return else: result.add(c) This is equivalent to defining two `readLine` procedures, one performing synchronous IO and accepting a `Socket` and another performing asynchronous IO and accepting an `AsyncSocket`. It works very well in practice.
- epage 7y agoThis is why I'm curious about algebraic effects which was recently discussed on /r/rust [0] The main challenges I see are around usability within the language design on how best to propagate and compose them. [0] https://www.reddit.com/r/rust/comments/cjcwmu/is_there_interest_in_algebraic_effects_in_rust/?st=jzeporyd&sh=99004b62 https://www.reddit.com/r/rust/comments/cjcwmu/is_there_inter...
- grayrest 7y agoI found out about algebraic effects a year or two ago when I ran across the efforts to bring them to OCaml. Async/await was a preliminary thing so I though algebraic effects would be a more complete solution and more in line with the Rust ethos. After asking around in some NYC meetups and on Reddit , my impression is that there isn't a lot of appetite to break additional new ground in terms of bringing fringe language features (I'm unaware of a non-academic language that features them in a production release, OCaml is closest AFAIK) into a language that already has a fairly high barrier to entry.
- otabdeveloper2 7y ago> I thought Rust had other, better ways to create non-blocking code so I don't understand why to use async instead. 'async' exists because Python has that GIL bullshit and so Python programmers had to invent that fifth wheel of 'async programming'. Programmers in other languages then got jealous because they, too, wanted a complex, unnecessary framework that pollutes the whole runtime and serves to differentiate regular programmers from 'rockstar' programmers. And so async got fashionable and barely-literate coders now think async is magic performance dust that will automatically make your program run 1000% faster. TL;DR - it's just fashion, give it five years and we'll be reading posts about how async sucks and that it's stupid legacy tech invented by bonehead dinosaurs.
- atombender 7y agoPretty sure the async/await support in C# predates Python. C# introduced it in 5.0, which came out in August 2012. The Python proposal (PEP 3156) for an async library was posted in 2012, the proposal (PEP 492) for async/await syntax in 2015, and implemented in Python 3.4 and 3.5 respectively, I believe. So C# predates Python by about 3 years. From what I can gather, Python was influenced by C#. But C# doesn't have a global lock, and that's not why it has async/await. Edit: Added PEP reference.