1. 45
  1.  

  2. 11

    As someone who uses JavaScript await [1], I like this syntax. I would rather do:

    fetch('http://alfa.com/bravo.json').await.json().await;
    

    versus what I currently do:

    await (await fetch('http://alfa.com/bravo.json')).json();
    

    [1] https://github.com/cup/umber/blob/master/tv/assets/main.js

    1. 0

      Pipe operator fixes this also:

      const someJson =
        'http://alfa.com/bravo.json'
        |> fetch
        |> await
        |> (x => x.json())
        |> await
      
      1. 1

        wait, is that doable with the upcoming pipe operator?

        1. 0

          There are a few different proposals, one of which would allow this.

      2. 0

        Why not do the usual, i.e.,

        const bravo = await fetch(http://alfa.com/bravo.json');
        bravo.json();
        

        ?

        There’s no ambiguity, no extra parentheses, and no keywords masquerading as member properties.

        1. 1

          Have you tried it? That’s not valid syntax inside an async function. Both methods return a promise, not an object.

          1. 0

            If you want to debate the specifics of the syntax, we can do that. But my main point is why not stick to the usual const x = await f() style that we normally use? Everything doesn’t need to get turned into an expression. It’s OK to break things up into multiple bindings.

            1. 1

              If you want to debate the specifics of the syntax, we can do that.

              Why would I debate someone who hasnt even tested the code they are publicly posting?

              But my main point is why not stick to the usual const x = await f() style that we normally use? Everything doesn’t need to get turned into an expression. It’s OK to break things up into multiple bindings.

              My actual code is this:

              let chan = await (await fetch('/umber/tv/assets/data.json')).json();
              

              a single line of 68 characters. This line presents no hallmarks of needing to be broken up:

              • overly long
              • deeply nested
              • repeated expressions

              so the only real reason to do as you suggest would be to hide the bad language grammar. Id rather not hide it, Id rather put the bad grammar on full display while at the same time praising Rust good grammar, as I did in my original comment. If you wish to hide away bad language decisions thats your choice.

              1. 0

                Why would I debate someone who hasnt even tested the code they are publicly posting?

                Because this is a discussion forum, not a PR up for review. I’m sure you can find plenty of those on GitHub.

                This line presents no hallmarks of needing to be broken up

                Well, there is one hallmark that people use to break up async code: to try to make it look more sequential. That’s the whole point of async/await syntax. If you want an expression syntax you can just use .then(...):

                fetch('/umber/tv/assets/data.json').then(data => data.json()).then(chan =>
                  ...);
                

                This is not that much longer than the await-expression sugared version, doesn’t require changes to the language, doesn’t require learning any new syntax, and as a bonus it even reads left-to-right in terms of the data flow. But it also doesn’t, like your version, do anything to ease understanding of the code in more sequential terms. Which is what every other async-await syntax out there is trying to do. Not ‘hide bad grammar’. But to re-arrange async or effectful code to look more linear so our linear brains can understand it with less effort.

                1. 1

                  The fact that you would propose a .then solution, coupled with your invalid first example that you never corrected, shows that maybe you dont have a strong grasp on asynchronous programming.

                  Async/Await was created in large part precisely to avoid constructs like .then. So having chose to use await for this exact reason, why would I now move back to .then?

                  1. 0

                    Oh, I see we’re pulling out the ‘You don’t understand XYZ’ already? In that case, perhaps you don’t have a strong grasp of how a logical argument works? I didn’t propose a .then solution, I said that the expression-level await syntax doesn’t offer a significant benefit over the existing .then solution. If all you’re doing is hiding a few lambdas, what’s the point? Imho it’s simply not a high enough improvement over the incumbent.

                    And speaking of grasping asynchronous programming, let me ask you one more thing, what are the semantics of the expression you originally posted?

                    fetch('http://alfa.com/bravo.json').await.json().await;
                    
                    1. 1

                      the expression-level await syntax doesn’t offer a significant benefit over the existing .then solution

                      …and youre flatly wrong about that. It offers the ability to write async code in a synchronous style. That is to say, it allows you to avoid the nesting required with typical async constructs like .then.

                      The fact that you have failed to demonstate understanding of this bedrock benefit of Async/Await is why I made my previous comment. A comment which apparently still stands.

                      1. 0

                        it allows you to avoid the nesting required with typical async constructs like .then.

                        What nesting? The typical use of .then is:

                        foo()
                          .then(bar)
                          .then(baz)
                        

                        The fact that you have failed to demonstate [sic] understanding of this bedrock benefit of Async/Await is why I made my previous comment.

                        Let’s face it, you keep putting me down because you think I don’t understand this like you do, and you get a power trip from hammering people for (what you perceive as) weakness.

                        You still haven’t answered my question though, what are the actual semantics of the expression-style await syntax sample you posted?

                        1. 1

                          you think I don’t understand this

                          You dont. If you did you would understand that in an example like this:

                          fetch('http://nghttp2.org/httpbin/json')
                          .then(q => q.json())
                          .then(z => console.log(z.slideshow.author));
                          

                          The resultant value z doesnt and cant exist outside of the final .then context. That means with large scripts, you have to put the entire rest of the script inside the final .then statement.

                          If you cant see why that can be problematic and/or undesirable, then I cant help you. With await the result can exist outside of the await call as long as within async function:

                          let z = await (await fetch('http://nghttp2.org/httpbin/json')).json();
                          console.log(z.slideshow.author);
                          

                          what are the actual semantics of the expression-style await syntax sample you posted?

                          Why would I bother going down this path with you when you dont even understand the underlying syntax of async programming with JavaScript?

                          1. 0

                            That means with large scripts, you have to put the entire rest of the script inside the final .then statement. … With await the result can exist outside of the await call as long as within async function:

                            Yes, because ‘within the async function’ is the entire rest of the script that you alluded to earlier. That’s how the syntax desugars. If your entire argument is that .then isn’t ergonomic for larger functions, then we’re already on the same page, my very first reply to you was about using await bindings in a normal style rather than the mish-mash await-expression style that you have here.

                            Why would I bother going down this path with you when you dont even understand

                            You’re still assuming I don’t understand syntax after saying things like ‘the final .then statement’? SMH.

                            1. 1

                              mish-mash await-expression style that you have here.

                              It is a mish-mash because of poor language design.

                              That is the foundation of my argument, one which you have just agreed to, thanks!

                              Rust did not make the same mistake, so kudos to them.

                              1. 0

                                The language design may be poor, but that usage of it is not idiomatic. In practice you will see the binding syntax which I have been talking about since the beginning of this thread.

                                I respect Rust’s decision given their context and constraints. In this thread I was talking about JavaScript syntax because that is what you brought up and that is what I replied to.

                                1. 1

                                  I never said that it is idiomatic, and I don’t care that it’s not. The syntax I gave is what should be used for the most basic of examples. Binding is only needed when:

                                  • a line gets overly long
                                  • a line is deeply nested
                                  • a line has repeated expressions

                                  otherwise youre just creating a variable for no reason other than “that’s what I’ve always done”. Sometimes it helps to think critically about what you’re doing instead of just blindly following idioms.

                                  1. 0

                                    And sometimes it helps to follow idioms which have been set up (usually for good reasons) instead of trying to mix in a new style and confusing people who might look at your code in the future.

                                    It helps to think about why you want to avoid ‘creating a variable’ when it’s already the idiom in the language you’re using. Do you not want a name describing the value? Why not? Do you think it will allocate more memory? It won’t, it will allocate the same.

                                    1. 1

                                      I made my reasons very clear. I am purposely using the syntax because:

                                      1. It is shorter
                                      2. Every expression is only used once, so creating variable is wasteful
                                      3. To demonstrate poor JavaScript language decisions
                                      1. 0

                                        I’m not sure how it’s coming off that I’m the one who’s angry. Projection, I guess.

                                        1. 1

                                          and there it is folks, he finally ran out of on topic arguments. Shame, I was hoping to go for a few more days at least. When youve lost the rational argument, attack the opponent!

                                          https://yourlogicalfallacyis.com/ad-hominem

                                          1. 0

                                            Dude, anyone can see that (a) I’m replying to something that’s not in your current comment, and (b) you edited your comment, presumably deleting the part that I did reply to. If Lobsters preserved comment history this would be really hilarious.

                                            1. 1

                                              Thats true, because its easier for you to comment on off topic matters, than to realize the obviousness of the truth: on topic, you dont have a leg to stand on, and you never did.

                                              1. 0

                                                I didn’t make this personal. I didn’t repeatedly accuse the other person of not understanding what they’re talking about, I didn’t edit my comments to try to hide what the other person replied to, and I didn’t turn what should essentially be a small difference of opinion in how to express some async code, into a flamefest. So, enjoy your imaginary internet victory! I’m all done here.

                                                1. 1

                                                  No, but what you did do was start a technical discussion with a profound lack of understanding of the topic at hand. Let us review your initial code:

                                                  const bravo = await fetch(http://alfa.com/bravo.json');
                                                  bravo.json();
                                                  

                                                  Youve got missing open quote, and a final statement thats NOOP as you havent attached it to a variable or a method with agency. On top of that youve demonstrated a lack of knowledge of async control flow as the final statement returns a Promise, not JSON data.

                                                  Its hard to take someone seriously when they enter the conversation with ignorance and lack of effort.

                                                  1. 1

                                                    Its hard to take someone seriously

                                                    No, it’s actually easy to have a technical discussion (on an internet forum, as I pointed out earlier) based on the actual merits of the argument (which you actually did do later on in the thread, no matter how begrudgingly), not on the syntactic nitty-gritty. What you keep doing here–putting down the other person with ‘It’s hard to take you seriously’, ‘You clearly don’t understand’, is the actual ad-hominem attack. Because the alternative would be to simply refute the actual technical arguments (which was plenty clear from the context), not focus on trivial details (again, in a discussion forum, not actual production code) to try to score some easy points.

                                                    Edit: also, it’s funny to read someone attacking my ‘ignorance and lack of effort’, and particularly a ‘missing open quote’, with a reply lacking actual possessive quote characters (‘youve’).

                                                    1. 1

                                                      As much as you might want me to, I’m not separating your code from your argument. I might do if you have provided a single coherent code block with arguments as secondary, but you have not done that.

                                                      A technical discussion of this nature starts with code. If you cant or wont demonstate your competency then I dont really care what your position is. Respect has to be earned, and your initial example is poor and you refused to correct it. If you had provided a working example with a different style i might have looked at it and gone “hey, thats not my style but it does work, maybe the guy has a point”.

                                                      You never did that and your reticence lends to that you cant. At this point I wouldnt bother though as the window you had to demonstrate your compentence closed about 5 comments ago.

                                                      Oh my god I just realized what you are, you are asking for me to draw 7 perpendicular lines LOL:

                                                      https://www.youtube.com/watch?v=BKorP55Aqvg

                                                      Essentially you are proposing to have a discussion of which you apparently have no technical knowledge, but then get upset when an idea in your head clashes with reality, because of said lack. Sad.

                                                      1. 0

                                                        The style that I referred to is widely used throughout ES6 codebases with async programming, as you yourself admitted by calling your own example un-idiomatic. No one reading this comment thread can be in any doubt as to what I’m talking about, if they take a couple of minutes to look up ‘JavaScript await syntax’. So I have no doubt whatsoever that you knew from the beginning what I was talking about. Your refusal to separate the code from the argument, your insistence on immediately assuming that I don’t know what I’m talking about based on a single code sample, speaks more about your inflexibility than about my expertise.

                                                        A technical discussion of this nature starts with code.

                                                        A technical discussion can start with technical points, it can start with code, or it can start with a mix of both. One side of the discussion doesn’t get to dictate the terms of the discussion–that’s not how forum discussions work.

                                                        But, since you worship code at the expense of all else, I will happily express the code that you originally pointed to ( https://github.com/cup/umber/blob/02dbee8c38d2cee729e1b5c2a85f70ae3e6b57f0/tv/assets/main.js ) in the more idiomatic style I referred to:

                                                        async function main() {
                                                          const data = await fetch('/umber/tv/assets/data.json');
                                                          const chan = await data.json();
                                                        
                                                          chan.forEach(ab => {
                                                          ...
                                                        

                                                        Note that I used ellipses to indicate the rest of the function despite knowing your preference for working code above all else. Hopefully that doesn’t set off a new wave of criticisms about my lack of knowledge!

                                                        1. 1

                                                          Sorry bro, but as I said your window to demonstrate competency is closed. If you had only posted this 13 comments ago it might have been worth reading!

                                                          Take care.

                                                          1. 0

                                                            Oh, of course, because there’s a time limit on demonstrating ‘competency’ in internet discussions, after which any demonstrations are null and void. I must have forgotten that when they were handing out the Internet Discussion Rules! Cheers.

                                                            1. 1

                                                              Its just simple rules of etiquette. Dont waste peoples time.

                                                              Instead of acknowledging your mistake and correcting it so that discussion could move forward, you refused and tried to move past it without providing a proper example as a starting point.

                                                              I dont like my time wasted, so I wasted yours. Maybe this can be a lesson to you in the future of how to handle technical discussions. Thanks.

                                                              1. 0

                                                                People may not like having their time wasted, but they also generally don’t go about calling others incompetent in discussion forums because of it. That’s also etiquette.

                                                                1. 1

                                                                  You get what you give.

                2. 0

                  Just curious, why not let chan = await fetch('/umber/tv/assets/data.json').then(r => r.json());?

                  Not that it matters much, I’m just wondering if you had considered that option and preferred the double await for some reason.

                  1. 1

                    The whole point of await is so that you can stop using .then, or as least have a more readable alternative. Proper .then syntax would be:

                    fetch(something).then(q => q.json()).then(THE ENTIRE REST OF YOUR CODE);
                    

                    await allows you to undo some of the required nesting used with .then. I suppose you could combine them if you wanted to, but do you really want to be making that kind of frankenstein code?

                    https://developer.mozilla.org/Web/API/WindowOrWorkerGlobalScope/fetch

        2. 14

          🚲🏚

          The repetition and length in threads like this is disheartening. There’s no particularly problematic post, which means that there’s no “ban this jerk and the problem will go away” solution. There’s no trolling here. Just a bunch of people that all want to be heard, creating a thread that is too long to read all of it, and the result is that none of them read it and they post the same thing ten times. It’s a maddeningly systemic problem.

          I’m really looking forward to hearing how the Meta WG plans to mitigate this kind of thing.

          1. 1

            Similar issue occurred in the Go tracker: https://github.com/golang/go/issues/29934

            1. 0

              What’s the value in letting anyone post anything on these threads? It’s not like the working groups don’t do a thorough search of the design space.

              1. 5

                We regularly get good ideas from such threads. Most discussions are rather on the side of not many voices.

            2. 4

              If you haven’t already read “What Color is Your Function?”, I highly recommend it: https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/

              I wonder why so many languages opted for async/await rather than threads. I understand that granting untrusted code the power to create threads is a risk, so at least in JavaScript’s case it makes some sense. But I find it curious that languages like Go are the exception, not the norm. (My own language also uses threads.)

              1. 18

                Rust has threads. The standard library API is right here.

                • Threads as they currently exist in Rust are just a wrapper on top of POSIX or Win32 threads, which are themselves implemented in the OS kernel. This means spawning a thread is a syscall, parking a thread is a syscall, jumping from one thread to another is a syscall*, and did I mention that the call stack in Rust can’t be resized, so every thread has a couple of pages of overhead? This isn’t a deal breaker if you wrap them in a thread pool library like rayon, but it means you can’t just use OS threads as a replacement for Erlang processes or Goroutines.

                • Green threads introduce overhead when you want to call C code. For one thing, if you make a blocking call in C land, green threads mean you’re blocking a whole scheduler, not just one little task. For another thing, that stack size thing bites you again, since it means your green threads all have to have enough stack space to run normal C code, or, alternatively, you switch stack every time you make an FFI call. Rust used to have green threads, but the FFI overhead convinced them to drop it.

                So, since green threads aren’t happening, and you can’t spawn enough OS threads to use them as the main task abstraction in a C10K server, Rust went with the annoying leaky zero-overhead abstraction. I don’t really like it, but between the three options, async/await seems like the least bad.

                * Win32 user mode threads can allow you to avoid task switching overhead, but the rest of the downsides, especially the stack size one, are still problems.

                1. 3

                  Great comment! Just want to nitpick about this:

                  Green threads introduce overhead when you want to call C code. For one thing, if you make a blocking call in C land, green threads mean you’re blocking a whole scheduler, not just one little task.

                  Regarding “blocking calls in C land”, using async/await with an event loop is not better than green threads: both will be blocked until C land yields control.

                2. 11

                  I wonder why so many languages opted for async/await rather than threads

                  I think you have to understand that this isn’t an either-or question. Rust, of course, has had threads for ages – the async/await RFC revolves around providing an ergonomic means of interacting with Futures, which are of course just another abstraction built on top of the basic thread API.

                  The better question would be, “What are the ergonomic issues and engineering trade-offs involved in designing threading APIs, and why might abstractions like Futures, and an async/await API, be more appealing for some sorts of use-cases?”

                  1. 2

                    I’m much more of a fan of algebraic effects for this stuff. Multicore OCaml seems to be moving in the right direction here, in a way that can reduce the splitting of the language in two. I would have loved to have seen something like this in Rust, but I can understand that the more pragmatic choice is async/await + futures. We still need to figure out how to make algebraic effects zero cost.

                    1. 1

                      Yeah. The problem is the language needs some sort of programmable sequencing operators built in that async primitives can make use of, while users can write code that is agnostic to them.

                      1. 1

                        OCaml has let+ syntax now (in 4.08) which addresses this.

                        1. 1

                          One example is how you can write:

                          map : ('a ~> 'b) ->> 'a list ~> 'b list
                          

                          which is sugar for:

                          map : ('a -[e]-> 'b) ->> 'a list -[e]-> 'b list
                          

                          where e is a type level set (a row) of effects. That way you can have a map function that works in pure situations, or for any other combination of effects. Super handy.

                          1. 1

                            That’s really, really nice!

                      2. 1

                        With the proper functions / operators (bind / lift) this is not much of an issue in practice.

                        1. 1

                          There’s certainly value in the greenthread solution, as evidenced by the success Go, but Rust’s approach makes much more control over the execution context possible, and therefore higher performance is possible. To achieve the absolute highest performance you have to minimize synchronization overhead, which means you need to distinguish between synchronous and asynchronous code. “What color is your function” provides an important observation, but we shouldn’t read it as “async functions are fundamentally worse”. It’s a trade-off.

                          Of course, prior to Rust, async functions didn’t give much (if any) control over the execution context, and so the advantages of async functions over greenthreads were less clear or present.

                          1. 1

                            I’m not 100% sure if that’s a good intuition that I have, but I kinda think that in case of Go, it’s more like “every line is/can be async-await” in it — because of the “green threads” a.k.a. goroutines model (where goroutines are not really your OS’s threads: they’re multiplexed on them — as will happen with async/await functions, IIUC).

                          2. 2

                            I’m not a fan of how async and await is forced on the programmer. This really feels like something that should be done as a compiler pass instead of making the user act like a compiler.

                            1. 2

                              Okay, the postfix .match sold me. I like the “dot await” syntax now.

                              1. 2

                                I’m particularly entertained by the results of the survey on the topic: https://www.reddit.com/r/rust/comments/bju8di/asyncawait_syntax_survey_results/

                                There’s no syntax that people particularly actually like, but all of them are widely disliked. Bit of a comment on human nature, there. So, this seems a perfectly reasonable choice.

                                We COULD just all use S-exprs and have parenthesis do all the hard work for us, but noooo, people don’t like that for some reason. (Honestly I find it gets rather clunky for simple things like array and struct references, as well as type annotations, but for anything more complicated/situational than it makes all these questions just magically evaporate.)

                                1. 0

                                  IMO lisp was written for computers not people. People read left to right, not inside to outside. After about 4 layers lisp becomes unreadable unless you start with formatting gymnastics. This is one of reasons for rise of python and yaml. People want something that is readable. Method chaining ala Ruby or JavaScript is dead simple to read.

                                  1. 7

                                    People read left to right

                                    Reminder that this isn’t even remotely universally true, you’re over-generalizing from one cultural viewpoint.

                                    Point being: if you’re going to make an argument from naturalness, make sure you don’t pick something that’s actually arbitrary. People are actually incredibly flexible about reading order. You’ll quickly find when learning Arabic that reading right-to-left presents no challenges, and similarly when learning East Asian languages that top to bottom, right to left is not discomforting either. Hell, Boustrophedon order (flipping between lines) isn’t hard either.

                                    The notion that Lisp is some unnatural, unmasterable order seems implausible. It’s largely left-to-right, and the right to left precedence hierarchy of thing-operated-on out to operations-on-thing is no different than the way English itself structures modifiers and objects

                                    1. 5

                                      People read left to right, not inside to outside

                                      @cup compared reading “left to right” with reading “inside to outside”. He/she could also have written “people read right to left, not inside to outside”, with the same conclusion. I’m not aware of any human language where sentences are read from “inside to outside”.

                                      1. 4

                                        “Reads inside to outside” is a poor description of the way that Lisp functions — it reads left to right, it’s just not strictly left associative. No human language is either. Certainly not English.

                                        Consider “gather the tall roses in the brown basket, quickly”. Does English read “inside out” because the things being operated on by the verb “gather” are specified right-to-left (subject: roses. What kind of roses? roses which are tall) in the middle of the sentence? Does it read right to left because the adjective “quickly” applies to the clause that comes immediately to its left?

                                        Obviously not.

                                        “Left to right” is about how we lay words out on a page, not how we construct meaning. As readers we parse the complicated structure of English in a very complex manner that involves relationships between words flowing in a variety of directions.

                                        We lay Lisp out left to right, similarly. Its operands might fall “in the middle” and be composed of complex clauses themselves, but again that’s hardly unique — most languages aren’t SVO, so at least one of Subject and the Object will be clause-internal. Even a classically SVO language like English has a variety of non-SVO forms, as above. In a language like Japanese, which I also speak, clauses are typically SOV, with clauses able to serve as subject or object in larger constructions (as in English), so you frequently see sentences of the form S(SOV)V.

                                        No sane person would claim Japanese reads “inside out”.

                                        (gather (roses :tall) (basket :brown) :quickly)) is no more “inside out” than English, or Japanese. It’s just a VSO style ordering, which again is permissible even in English. It’s also the default ordering in Arabic, welsh, filipino,…

                                        1. 2

                                          Interesting. I agree that Lisp is mostly read left-to-right, top-to-bottom. I just have to keep in mind some kind of stack to know where I am in the nested parentheses, but it’s a matter of habit, and not different from parsing an English sentence, as explained in your comment. Thanks for the thorough comment :-)

                                    2. 1

                                      Lucky for me then my next most favorite language is Forth. :D

                                  2. 1

                                    I still hope the official async/await will be convenient outside the usual network blocking stuff.

                                    1. 1

                                      Does Rust have a built in event reactor?

                                      1. 3

                                        No, library only.

                                        1. 2

                                          How does async/await interact with a library reactor?

                                          1. 7

                                            Function bodies that are async are compiled into anonymous state machines that implement the Generator trait. Types of this trait can indicate whether they are still running or finished.