Earlier quoted context omitted.
Await is a keyword, syntax highlighters will most likely paint it differently.
I find the syntax highlighting argument odd. It's like arguing a road surface is acceptable because the dangerous conditions it causes are mitigated by traction control.
Update on await syntax in Rust
141–150 of 199 posts
Re: Update on await syntax in Rust
#142Earlier quoted context omitted.
I'd submit the key difference in Haskell syntax is actually currying, not laziness. Haskell syntax privileges currying, and an executed function is just a curried function that has all of its parameters. By contrast, currying in languages with Algol-descended syntax always requires more rigamarole. It's possible, of course, in a lot of them, but it's harder than just a function call missing some of its arguments.
> By contrast, currying in languages with Algol-descended syntax always requires more rigamarole. It's possible, of course, in a lot of them, but it's harder than just a function call missing some of its arguments. Really? Given a Python function def divide_by(a, b): return a / b What would be so hard about divided_by_2 = divide_by(, 2) divide_5 = divide_by(5, ) Or, in Rust: fn divide_by(a: f32, b: f32) -> f32 { a /…
Well, first of all, it doesn't work right now, which is a problem for what I was trying to say.
Secondly, if you want to curry things, you'll end up with
four_arg_func(1, 2)(3)(4)
at a bare minimum as a "curried application", where in Haskell it's just four_arg_func 1 2 3 4
Several Algol languages have other issues, such as dealing with optional arguments: in Python!curryable, is the result of def f(a, b, c = 10):
return a + b + c
f(1, 2)
a function that accepts one more parameter for c, or the number 13? (Even in a dynamic language you ought to think twice before trying to return some sort of "quantum superposition" of those two things!) You'll need to add some syntax that will specify the answer to that question, and now Python!curryable is getting away from just "incompletely applying the function" as it is in Haskell, but now a Thing you have to Do. (Probably by calling https://docs.python.org/2/library/functools.html#functools.p... .)Haskell hacks that away by making it so functions take a fixed number of parameters, in a fixed order, and there is no such thing as default parameters to a function. A non-Haskell programmer may feel this is not a trade worth making.
Your closure at the end is what it tends to really look like. You'll note that most non-functional code doesn't really do that sort of thing very often, unless you're unlucky enough to stumble across a codebase written by someone trying to write Haskell-in-Python or something.
Re: Update on await syntax in Rust
#143Earlier quoted context omitted.
I find the syntax highlighting argument odd. It's like arguing a road surface is acceptable because the dangerous conditions it causes are mitigated by traction control.
I find the don't-consider-syntax-highlighting-at-all argument odd. It's like arguing traffic lights shouldn't use colors to indicate stop-and-go because some people either cannot perceive the color difference or choose to wear glasses that negate the color difference. (Analogies here suck. Instead, consider that the vast majority of programmers read code that has been syntax highlighted. To completely discount that e…
There is a reason that we have three separate lights still instead of one (that is, there's more reasons than just inertia), and that's because some people can't tell the color differences. Specifically, my father can't easily tell the color difference between green and yellow. I've had friends that also had this problem. It's not uncommon.
> To completely discount that experience at all would be odd
I'm not saying to discount it entirely, but consider it in context. I think it's a somewhat elitist argument, because it assumes everyone both has syntax highlighting set up and that their highlighting will work as well as yours.
I accept that syntax highlighting helps, and is a positive if it can be applied well to a solution, I just think it's far from sufficient.
Put another way, an optional, environment specific configuration setting is not sufficient to offset the negative aspects of an official language level feature, if we're able to weigh things prior to being implemented.
If Rust were required (or even officially expected) to be written in an environment where that was always available and easily configurable, I would think different. E.g. if we're talking about Visual Basic, or Smalltalk with it's IDE (IIRC, not that I have experience with it), then I would think different, but as long as the Rust compiler is expected to take in files of text and not some rich format that includes extra metadata, I don't think language design choices should weigh non-text considerations too heavily.
Re: Update on await syntax in Rust
#144Earlier quoted context omitted.
I find the syntax highlighting argument odd. It's like arguing a road surface is acceptable because the dangerous conditions it causes are mitigated by traction control.
I think it's totally reasonable to judge a road surface based on how effective it is, in practice, with the kinds of traffic that will be travelling on it. Traction control is a part of that landscape. Designing with awareness of syntax highlighting makes sense. But we should also keep an awareness that it won't always be there.
Yes, and if we outlawed the use of cars without traction control on certain roads, then I would be fine with designing with the assumption traction control will be on. But as long as you allow older cars without traction control to still drive on the road, you are making the roads less safe for a certain percentage or class of drivers. I view that as different that just making the road safer for some.
> But we should also keep an awareness that it won't always be there.
Exactly. And this is why I think it doesn't make sense as a mitigation to a language level feature which will always be there.
Specifically, I think syntax highlighting is a useful feature and a plus for a comparison, I just don't think it works as a mitigation for a negative for something that pervasive.
All other things being equal, syntax highlighting abilities might push me towards one solution over another, but they won't make me ignore a problem I perceive with a solution.
Re: Update on await syntax in Rust
#145I'm not familiar around the syntax, but I'm interested to know how much of this was a religious battle and how much of it results in meaningful complications for user code down the line. I've heard really good things about the Rust community (like nothing bad at all), until maybe two weeks ago when somebody mentioned this stuff. I'm a really big fan of not having religious battles. Is this syntax for native coroutine…
> I'm interested to know how much of this was a religious battle and how much of it results in meaningful complications for user code down the line. Due to the connotations of "religious battle", it's hard to get a good answer here. Nobody wants to be painted in this light. > Is this syntax for native coroutines? No. > Can it be combined with existing user and stdlib syntax? Yes. > What pathways for syntax developmen…
My impression of async/await is that it’s one part of making concurrency grokkable, and I’m not sure whether the debate was taking all that into account: https://vorpus.org/blog/some-thoughts-on-asynchronous-api-de...
Re: Update on await syntax in Rust
#146Question for people more familiar with async/await semantics, how do you typically control what thread the async procedure runs on? Having spent a lot of time now in rxJava and really getting into the power of stream processing and composition, it feels like async/await is almost too simplistic.
In .NET it defaults to scheduling continuations in the "main" thread as much as possible, with the easiest escape valve being Task.ConfigureAwait(false) that tells it that it can finish continuations wherever they land in the ThreadPool. This is why you can often find a lot of (partly mistaken) advice in .NET to always use ConfigureAwait(false) for "performance" which as often as not leads to people rediscovering the hard way why the easy default is continuations on Main/UI threads. There actually is a more complex TaskScheduler mechanism underlying that big ugly valve, but TaskScheduler is not quite as featureful or easy to control as the RX-equivalent scheduler.
(I heard some rumblings that the .NET team was considering in the long run to better unify async/await TaskScheduler scheduling with the Scheduler model of RX.NET, but I don't know if anything has progressed yet along those lines.)
Python mostly requires manual management of event loops for async/await coroutine scheduling. Most Python code, even/especially using async/await is still generally single-threaded (due to artifacts like the GIL [Global Interpreter Lock] that are getting better with each passing version of Python), so more complicated scheduling is both generally not necessary and also left as an exercise to the user.
ECMAScript 2017 (JavaScript) async/await is also mostly running in single threaded event loops (though not manually managed like Python; generally browser UI event loop managed).
Re: Update on await syntax in Rust
#147Earlier quoted context omitted.
Syntax may be irrelevant at the end of the day, but nice syntax can make a big difference in usability imo. I'm not following the Rust example, but discussions of it remind me about discussions about UFCS (Universal Function Call Syntax). That's where `foo(a, b)` can be rewritten as `a.foo(b)`. That may seem minor, but look at this code: half_square = divide(square(a), 2) In comparison to: half_square = a.square().di…
Interesting example...I find the first much easier to read and reason about. ¯\_(ツ)_/¯
I don't think either is better but one is definitely familiar.
Generally I try not to care about syntax too much (except putting $ on the front of a variable, that annoys me not so much because they did but because not everyone did so every time I switch from PHP to TypeScript I end up putting $ on at least once a day), the weird part is that I write idiomatic code in both so my brain knows it shifted context but I still put the $.
let $foo = bar;
Just looks wrong.Weirder still when I run into code written by people who do
let $foo = $(foo);
For jQuery stuff (I totally get the reason why).Brains are funny things.
Re: Update on await syntax in Rust
#148A bit off-topic: Is there any theoretical reason you need async / await syntax at all? (It's certainly desirable for performance and compatibility to avoid making all subroutines into coroutines, so I understand why most languages have done this.) And restricting it to the case where coroutines have a single return... Subroutines are naturally coroutines that don't yield. And it seems like the question of whether it'…
I think (but I cannot find a reference to now) one of the proposed syntaxes was that awaits were immediate and implicit in async functions, and that if you didn't want that, you could opt-out and get futures normally. Loosely, async fn foo() { let result = blocking_req(); // awaiting immediately here. async { let future = blocking_req(); // not awaited. // can do more complex stuff w/ the future } } IDK what happened…
One of the goals behind it was to align async control flow with sync/thread-based control flow, to make it more intuitively obvious when things would run concurrently and when they would not. That is, calling a function, even if async, would immediately run it to completion, while wrapping it up in a closure or async block that gets spawned would run it concurrently.
This is especially relevant to Rust, which departs from other implementations of async/await in that merely constructing a future is insufficient to start it running, which is why you need that `join3` call to explicitly introduce concurrency.
Under the "implicit await" proposal, your first example would look like this:
async fn foo() {
let result = blocking_req(); // await immediately
let future = async { blocking_req() }; // think of async{} like a lambda function
// can do more complex stuff with the future, like:
// future.await()
// Future::join(future, other_future)
// tokio::spawn(future)
}
The thread-based equivalent looks very similar: fn foo() {
let result = blocking_req(); // block the current thread immediately
let closure = || { blocking_req() }; // don't run this yet
// can do more complex stuff with the closure, like:
// closure()
// rayon::join(closure, other_closure)
// thread::spawn(closure)
}Re: Update on await syntax in Rust
#149A bit off-topic: Is there any theoretical reason you need async / await syntax at all? (It's certainly desirable for performance and compatibility to avoid making all subroutines into coroutines, so I understand why most languages have done this.) And restricting it to the case where coroutines have a single return... Subroutines are naturally coroutines that don't yield. And it seems like the question of whether it'…
I think (but I cannot find a reference to now) one of the proposed syntaxes was that awaits were immediate and implicit in async functions, and that if you didn't want that, you could opt-out and get futures normally. Loosely, async fn foo() { let result = blocking_req(); // awaiting immediately here. async { let future = blocking_req(); // not awaited. // can do more complex stuff w/ the future } } IDK what happened…
I think with the `return alpha + beta + gamma` the idea is that within a scope, everything is done as late as possible, but you can't leave a scope without all results.
Thus, if the code is expanded as:
alpha = run_a()
beta = run_b()
gamma = run_c()
a_plus_b = plus(alpha, beta)
sum = plus(a_plus_b, gamma)
return sum
In that case, the compiler has to determine a partial ordering: a_plus_b
Thus it can generate: await gather(run_a, run_b, run_c)
await plus(alpha, beta)
await plus(a_plus_b, gamma)
And presumably you can detect pure subroutine / primitive invocations.And there would be other hard points where tasks must be fully awaited; e.g. loops wouldn't be able to leak tasks.
Re: Update on await syntax in Rust
#150A bit off-topic: Is there any theoretical reason you need async / await syntax at all? (It's certainly desirable for performance and compatibility to avoid making all subroutines into coroutines, so I understand why most languages have done this.) And restricting it to the case where coroutines have a single return... Subroutines are naturally coroutines that don't yield. And it seems like the question of whether it'…
Futures / async-await is an idiom that makes async code easier to reason about, not something that provides any new theoretical foundation or any functionality that wasn't possible before. Like a type system, it's strictly for safety & convenience.
Whether & where you wait on any individual async request is largely separate from the await syntax. If you can start 3 async requests in parallel, and await on the group rather than each request, you probably should.
That said, the idiom of gathering results sequentially is guaranteed to work and be safe, where hidden dependencies and hard to identify race conditions have plagued attempts to make parallel async code since the beginning of time. The syntax of futures in various languages is making async and parallel code easier and safer for most programmers to use without landing in quicksand.