Pauan
Async code can scale essentially infinitely, because it can multiplex thousands of Futures onto a single thread. And you can have millions of Futures multiplexed onto a dozen threads.
This makes async ideal for situations where your program needs to handle a lot of simultaneous I/O operations... such as a web server:
http://aturon.github.io/blog/2016/08/11/futures/
Async wasn't invented for the fun of it, it was invented to solve practical real world problems.
I agree that being able to use `async` inside of traits would be very useful, and hopefully we will get it soon.
> generally needing more capability to existentially quantify Future types without penalty
Could you clarify what you mean by that? Both `impl Future` and `dyn Future` exist, do they not work for your use case?
> Async function types are a mess to write out.
Are you talking about this?
fn foo() -> impl Future<Output = u32>
Or this? async fn foo() -> u32
> More control over heap allocations in async/await futures (we currently have to Box/Pin more often than necessary).I'm curious about your code that needs to extensively Box. In my experience Boxing is normally just done 1 time when spawning the Future.
> Async drop.
That would be useful, but I wouldn't call the lack of it "half-baked", since no other mainstream language has it either. It's just a nice-to-have.
> Better cancellation.
What do you mean by that? All Futures/Streams/etc. support cancellation out of the box, it's just automatic with all Futures/Streams.
If you want really explicit control you can use something like `abortable`, which gives you an AbortHandle, and then you can call `handle.abort()`
Rust has some of the best cancellation support out of any async language I've used.
> Async iteration.
Nicer syntax for Streams would be cool, but the combinators do a good job already, and StreamExt already has a similar API as Iterator.
thread_local! exists, and you can just call spawn_local on each thread. You can even call spawn_local multiple times on the same thread if you want.
You can have some parts of your programs be multi-threaded, and then other parts of your program can be single-threaded, and the single-threaded and multi-threaded parts can communicate with an async channel...
Rust gives you an exquisite amount of control over your programs, you are not "stuck" or "locked in", you have the flexibility to structure your code however you want, and do async however you want.
You just have to uphold the basic Rust guarantees (no data races, no memory corruption, no undefined behavior, etc.)
The abstractions in Rust are designed to always uphold those guarantees, so it's very easy to do.
I've used Rust async extensively for years, and I consider it to be the cleanest and most well designed async system out of any language (and yes, I have used many languages besides Rust).
We're talking about the comparison between using an abstraction vs not using an abstraction.
When I said "doesn't have a runtime cost", I meant "the abstraction doesn't have a runtime cost compared to not using the abstraction".
If you want your computer to do anything useful, then you have to write code, and that code has a runtime cost.
That runtime cost is unavoidable, it is a simple necessity of the computer doing useful work, regardless of whether you use an abstraction or not.
Whenever you create or use an abstraction, you do a cost-benefit analysis in your head: "does this abstraction provide enough value to justify the EXTRA cost of the abstraction?"
But if there is no extra cost, then the abstraction is free, it is truly zero cost, because the code needed to be written no matter what, and the abstraction is the same speed as not using the abstraction. So there is no cost-benefit analysis, because the abstraction is always worth it.
So I assume you mean "implementation complexity" but that's irrelevant, because that cost only needs to be paid once, and then you put the abstraction into a crate, and then millions of people can benefit from that abstraction.
https://docs.rs/tokio/1.32.0/tokio/task/fn.spawn_local.html
There is a lot of misinformation in this thread, with people not knowing what they're talking about.
Every executor (including tokio) provides a `spawn_local` function that spawns Futures on the current thread, so they don't need to be Send:
https://docs.rs/tokio/1.32.0/tokio/task/fn.spawn_local.html
I have used Rust async extensively, and it works great. I consider Rust's Future system to be superior to JS Promises.
Rust gives you channels (both synchronous blocking channels and async channels), and they work great, there is nothing stopping you from using them.
That makes abstractions far more useful and powerful, since you never need to do a cost-benefit analysis in your head, abstractions are just always a good idea in Rust.
Rust has changed a lot in the past 5 years, people just haven't noticed, so they assume that Rust is still an old outdated language.
Basically Rust Futures is what Go wishes it could have. Rust made the right choice in waiting and spending the time to design async right.
First, wasm-pack is not Ashley's personal project, it is an official Rust Wasm project. That means it is owned by the Rust Wasm team, and it is maintained by the Rust Wasm team. It is an official part of Rust, similar to how cargo and rustdoc are an official part of Rust. wasm-pack was never intended to be maintained only by Ashley.
Multiple Rust Wasm Core team members (including myself) politely asked Ashley multiple times to transfer publishing rights to the Rust Wasm Core team (which she was supposed to have done months ago), but she refused.
Multiple people had politely offered to take over maintenance of wasm-pack (when it was clear that Ashley was unwilling to do so), but once again she refused. She knew how important wasm-pack is to Rust Wasm, but she did not want to give up control and power, even though it wasn't even supposed to be her package in the first place.
Second, forking is not as simple or as easy as you claim... forking is something that has a very high cost, so it should be done as a last resort. wasm-pack is an official Rust package (and it is vital to Rust Wasm), and so forking it would have a lot of costs:
* A new crate would have to be created (what should it be called? wasm-pack2?)
* The GitHub repo would have to be changed or transferred.
* Multiple different documentation websites (including the official Rust website) would need to be updated.
* A newsletter would need to be sent out informing everybody of the change.
* All existing projects would need to switch to the new package.
* The old package would still exist, which would be very confusing for people, especially because many tutorials and blogs would still be referring to the old wasm-pack!
* Ashley herself would throw a huge tantrum over it, because she would view it as taking control away from herself. And because she is a Rust Core team member, her tantrum would have power behind it.
Forking is absolutely NOT an appropriate solution in this case. Ashley's behavior was simply wrong, unacceptable, and reflects very poorly on the Rust team (which she is a part of).
As for Ashley's personal character... before she worked for Rust, she worked for npm. While she was working there, she tried to falsely accuse Rod Vagg because she wanted to kick him out of npm. Thankfully she failed, and after she failed she quit npm:
https://thenewstack.io/node-js-forked-complaints-repeated-ha...
https://medium.com/@rvagg/the-truth-about-rod-vagg-f063f6a53...
While she was working for npm, she violated npm's Code of Conduct numerous times, saying incredibly horrible sexist and racist things such as "kill all men", and actively trying to prevent white men from speaking at tech conferences:
No, she was not joking, and even if it was "just a joke" it is unacceptable. If a man said "kill all women" even as a joke he would be immediately fired and blacklisted from all companies.
Despite all of this, she was still hired onto the Rust Core team, because she is in a romantic relationship with Steve Klabnik (nepotism). Interestingly, Steve Klabnik is also the same person who is smearing Amazon because Amazon denied a job to Ashley.
The Rust Core team was aware of Ashley's past behavior, yet they hired her anyways. And even though numerous people spoke out about this, they were silenced and censored by the Rust team:
https://internals.rust-lang.org/t/announcement-ashley-willia...
Ashley also abused her moderator powers to ban dakom, even though he had done nothing wrong:
https://github.com/rustwasm/wasm-pack/issues/914
https://github.com/rustwasm/wasm-pack/issues/928
She also made numerous lies in those two threads (such as claiming that the Rust Wasm Core team is "random people without organization", the Rust Wasm Core team is hand-picked, they are the official leaders of Rust Wasm).
She acted incredibly disrespectful toward the Rust Wasm team (who worked very hard to make Rust work on Wasm), even though she had contributed basically nothing.
There is a dark side to Rust, which everybody is afraid to talk about. Anybody who tries to discuss things is censored by the Rust Core team. That's why I stopped contributing to Rust and I will never go back.
Nulan's macro system is very similar to Common Lisp or Arc's macro system, in the sense that a macro is simply a compile-time function that accepts code and returns code.
That means you can use all of the standard stuff within a macro: loops, filters, maps, etc.
Nulan achieves hygiene with two different techniques:
1) Gensyms. This is a standard technique in Common Lisp and Arc. But it only works for locally-defined variables.
2) All global variables are replaced with gensyms, and when a macro expands to a global variable, Nulan will automatically replace it with the appropriate gensym.
This is similar to Common Lisp's package system.
----
Here is an example of a Nulan macro:
(REWRITE-RULE
| (AND ~a ~b)
&(IF ~a
| ~b
| false)
| (AND ~a ~@b)
&(AND ~a (AND ~@b)))
This says that:(AND 1 2) should be replaced with (IF 1 2 false)
(AND 1 2 3) should be replaced with (IF 1 (IF 2 3 false) false)
(AND 1 2 3 4) should be replaced with (IF 1 (IF 2 (IF 3 4 false) false) false)
----
The & syntax is similar to ` in other Lisps except that it replaces global variables with gensyms.
The ~ syntax is similar to , in other Lisps.
The ~@ syntax is similar to ,@ in other Lisps.
----
Here is an alternate way of writing the AND macro:
(REWRITE-RULE
| (AND ~a ~b)
&(IF ~a
| ~b
| false)
| (AND ~@a ~b)
(reduce-right b a -> b a
&(AND ~a ~b)))
The reduce-right function is the same as foldr in Haskell, or fold-right in Scheme.This demonstrates that we can run arbitrary code at compile-time.
----
Although Nulan macros are hygienic by default, you can intentionally break hygiene when you need to, by inserting symbols:
(REWRITE-RULE
| (AIF ~a
| ~b
| ~c)
&(LOCAL
| ~(SYMBOL "it") <= ~a
\ (IF ~(SYMBOL "it")
| ~b
| ~c)))
The above macro is the same as Arc's aif macro. You use it like this: (AIF foo
| (bar it)
| (qux it))
Essentially, it is identical to IF, except that it binds "foo" to the local variable "it", which can then be used inside the body of the AIF.In general, Nulan macros are just as convenient and powerful as Common Lisp or Arc macros.
----
For a list of some of the changes, please see this post:
There were some bugs in the tutorial, which I fixed just now. If the tutorial wasn't working for you, please try again.
----
This is a pleasant surprise: I honestly was not expecting this, since Nulan is not yet ready to show off.
As explained on the GitHub page, Nulan is currently in development, and does not work.
The tutorial is for a very old version of Nulan from January 2013.
From 2013 to 2016, Nulan has changed dramatically. The latest design of Nulan barely resembles the tutorial.
Probably the biggest change is that Nulan is now statically typed rather than dynamically typed. Its type system is very similar to Haskell, SML, OCaml, etc.
Nulan is a purely functional language, like Haskell. Impure I/O is done using the Task monad.
Nulan also has a feature which is very similar to Haskell's typeclasses, giving a great amount of extensibility, flexibility, and abstraction.
----
The current goal of Nulan is to merge the benefits of Haskell and Lisp, while compiling to very efficient JavaScript code which can run in either the browser or Node.js
I put a heavy emphasis on readability, correctness, performance, and refactoring. I want Nulan to be the kind of language which makes refactoring programs very easy.
----
If you want a taste of the latest version of Nulan, I recommend taking a look at this file:
https://github.com/Pauan/nulan/blob/f008287f9ddeeffa0f20aa0d...
It lists all of the major features which are currently in Nulan's design. Things are still in major flux, and are constantly changing.
----
Nulan has been designed to make it easy to create high quality libraries, and it includes a robust standard library, which you can find here:
https://github.com/Pauan/nulan/tree/f008287f9ddeeffa0f20aa0d...
As proof of how rapidly Nulan is changing, some parts of the standard library are using older Nulan syntax. They will eventually be updated to the latest syntax.
----
Nulan also has an FFI system, which makes it possible to use JavaScript libraries in Nulan, bypass the static type system, add new features, or gain additional performance.
A significant amount of Nulan's standard library is written using the FFI, which you can find here:
https://github.com/Pauan/nulan/tree/f008287f9ddeeffa0f20aa0d...