Hacker News new | past | comments | ask | show | jobs | submit login

> no async/await

Doesn't this mean we're basically back to callback hell? This was one of the primary criticisms of Node before async/await was introduced.




Callback hell was before the introduction of Promises. Promises tried to abstract away the callback mechanism and it made things a little clearer, but you still had the pyramid effect of successive Promises.

Await/Async kind of streamlined all this.


I still somewhat prefer the readability of promises vs. await. I like the expressiveness of a then/catch as functions as opposed to try/catch blocks, which I‘ve always disliked. But I agree that await is more suitable for more complex scenarios.


async/await is just a syntax sugar, most languages didn't have it until very recently when it became a trend or something. In OCAML it's just like any other monad you use the bind operator instead. No need for special keywords.


C# 5.0 introduced async/await in mid-2013 (late 2012 depending on how you count it), and F# had it before that in its version 2.0 (~2007). It's a pretty old "trend".


anything else other than those 2? An idea that .Net started, could be good could be bad, but the .Net association isn't added credibility.

async/await aren't keywords in F# and kotlin


> anything other than those two?

Rust?


I was arguing that languages are following the "add async/await" trend recently, including rust. That other comment brought up that it exists in C# for a long time, hence not a recent trend.


https://en.wikipedia.org/wiki/Async/await#History

F# in 2007

C# in 2012

Python in 2015

Typescript in 2015

Javascript in 2017

Rust in 2019

C++ in 2020

Swift in 2021


I'm almost sure that Haskell has had its "do notation" even before 2007.

And maybe even Scala its "for comprehension".


Yeah - I was limiting my post to explicit async/await.

Don Syme, creator of F#, had a nice thread on the roots of async/await, though it doesn't mention do-notation: https://mobile.twitter.com/dsymetweets/status/14074008179179...

I'm still annoyed that most other languages stole async/await... but not the more general monadic/do notation. Instead we get elvis operators, chaining functions, etc.


F#'s "Computation expressions" are the same sort of thing as the mentioned Haskell's "do notation" and Scala's "for comprehension". They're just thin syntactic sugar for "callbacks" -- chaining maps/binds/flatmaps/etc. Even though F#'s "Computation expressions" are very cleverly thought out and more general.

https://docs.microsoft.com/en-us/dotnet/fsharp/language-refe...

Async/await for example in C# or Rust is different. This translates the program into transition automata/coroutines.


in F# it's an instance of computational expression, not keywords. So it's really only C# that added the keywords, and other languages followed suit since 2015. None of the functional languages have async/await as keywords


> it's just like any other monad

Ah yes. The magic word "just". You "just" do something that does something.

And yet even the example in the article uses Promise.then and eventual callback hell.


I have to admit I don't know much about ReScript and only have very basic exposure to OCAML, here is how you do await in it:

https://github.com/ocsigen/lwt

The `let* in` is a generic syntax for monads, it doesn't need a special one just for promise. This was in fact a debate back when async/await was in consideration for ECMAScript, but special syntax is hip so now we have `async/await` for Promise, `.?` for optionals and `flatMap` for arrays, basically the same thing.


Having seen too much `.map(_.map(` in Scala, I'd argue that different words/syntax for different things (that happen to have the same algebraic structure) is a good thing.


yes but they don't have to be keywords or magical syntax, can be defined in user land


> here is how you do await in it

That's how you do "await" in a third-party library that brings with it a lot of other baggage.

And I doubt it works in ReScript.

> The `let* in` is a generic syntax for monads

Ah yes. "Just" putting `let*` there magically makes promises work, is that what you're saying?

It turns out Lwt gained support for let* sytax only in April 2020. Stupid them. They didn't know they just had to use it.


> It turns out Lwt gained support for let* sytax only in April 2020.

I don't understand your point. Monadic let can be done with ppx_let (created in 2016), and even before that there was the older syntax where you write `lwt` instead of `let`.

https://github.com/ocsigen/lwt/commit/d28204984646042676570d...


> I don't understand your point.

How it started: "In OCAML it's just like any other monad you use the bind operator instead. No need for special keywords."

How it's going: "well, there's a third party libary that only recently introduced let*, but before that you had to use either ppx_let [whatever that is — d.] or an older where you use lwt"

You listed no less than three special keywords. And one of them specifically introduces a new syntax via ppx.

"Just".

I find this overuse of the word "just" extremely infuriating. And FP-community is especially guilty of overusing it. "Oh, no need for X, just use <some string of smart-sounding words that is always extremely convoluted, doesn't actually do what X does, comes with caveats the length of the equator etc.>"


the point is not that there should be no special syntax, but that special syntax in these (functional) languages serves a more generic purpose, and isn't just for async.

Ocaml is actually the worst example because it has always relied on extensions to provide an alternative to Haskell's do notation.

> well, there's a third party library that only recently introduced let*

it won't change how you feel about it but `let*` is an syntax extension (ppx), kinda like babel transform. It has nothing to do with the third party library. What the library provide are async primitives, equivalent of the `q` package in nodejs


I can only quote the sibling comment: https://news.ycombinator.com/item?id=29918575

--- start quote ---

Your point about using "just" is an important one. When you're inside all of that, it seems crystal clear, but for people coming outside of that bubble, it can be quite opaque.

--- end quote ---

So, your original comment, " In OCAML it's just like any other monad you use the bind operator instead. No need for special keywords." quickly devolved into:

- oh, it's not "just a bind operator"

- oh, you need special syntax

- oh, you need a third party library that may or may not have support for that special syntax

So yeah, it's not "just <intellectually superior sounding words>". It never is.

Especially in the context of the conversation which is:

- ReScript

- Requirement to integrate and interoperate with JS and JS libraries that are increasingly Promise-based.


I get it the first time, no need to repeat.

If you read back my comment, at one point I said I only have basic understanding of OCAML, at no point I claim to be an expert. I'm mediocre in programming in general. What I said I said it from the perspective of a javascript developer who don't think that async await is that big a deal, I just happen to know some context regarding functional programming, which, again, isn't the point of view I am taking, since I only have basic knowledge and write javascript at my day job.

With that said, let me put it bluntly, you are being annoying.

If we want to stop talking about programming and start picking on people's tone, let me point out one thing: you are not a mind reader, the way you just pick on a rather common word that people often use casually, and infer that I am asserting my "intellectually superiority", is annoying. Let me remind you this is the Internet where everyone is mostly anonymous. No one cares enough to show off to a bunch of usernames that don't matter to them.

You are right to say that what I claimed to be simple, wasn't simple, but I'm not taking the other nonsense you are accusing me of.


> - oh, it's not "just a bind operator"

It is though, it's a function/operator that is defined in userland.

> - oh, you need special syntax

> - oh, you need a third party library that may or may not have support for that special syntax

No, you don't need either of these things. These just add a convenient syntax that desugars to the aforementioned bind function. These PPXs aren't special keywords specifically for async/await that had to be added into the language's syntax itself. Note that I know nothing about ReScript and this reply is not about ReScript, as that's not what this comment chain is about.


The full explanation: In OCaml, before 4.08, there was no special support for monads. For example, the Option module (https://ocaml.org/api/Option.html) offers an "Option.bind" function. When chaining monads, you would usually write "Promise.bind" for promises, "Option.bind" for options, etc. OCaml also allows you to define your own infix operators. By convention, "bind" is often associated with ">>=". But this is usually done in userland code, OCaml and its standard library don't use ">>=". For example, the Result module (https://ocaml.org/api/Result.html) uses "Result.bind" too, but doesn't define ">>=".

Lwt is one of the two popular libraries used to have asynchronous IO, the other being Async. It does this through a type called "Lwt.t", which is very close to promises in JS. Lwt.t is a monad, and to chain monads like normal code, you need to use "bind". In OCaml, you can chain expressions with ";" or "let ... in", but if you want to chain monads, you have to use "bind" or ">>=" instead. This is considered annoying by some people, and those people often want monadic code to look the same as regular code. For this, Lwt and Jane Street (the company behind Async) have syntax extensions, called "PPX". PPXs are OCaml code that is executed on your code before your code is compiled. You could call them macros. The equivalent of PPXs in the JS world would be Babel, and a single PPX would be a Babel transformer.

In 4.02, Lwt added the Ppx_lwt library, that allows you to write code that is close to regular code, but is monadic: "let%lwt ... in" instead of "let ... in". There's also ppx_let, by the people behind Async: "let%bind ... in".

That brings us to binding operators. In 4.08, OCaml added support in the base language (so no need for PPXs) for monadic let. Here's the manual page about it: https://ocaml.org/manual/bindingops.html. So now, you can write "let* ... in", which is a bit shorter, and doesn't rely on external libraries or PPXs. You can see in the manual page I linked in the 2nd and 3rd code blocks examples how this makes monadic code look more like regular code. There's also "let+ ... and+ ... in", which has support for applicative functors.

Your point about using "just" is an important one. When you're inside all of that, it seems crystal clear, but for people coming outside of that bubble, it can be quite opaque. To finish on another JS example, this story is close to the story around asynchronous code in JS. First callbacks, then promises, then syntaxic support in the language to make promise-heavy code look just like regular code. The difference here is that OCaml put a more general async/await in the language, but no promises.

To go back to ReScript, you can use promises in ReScript, but there is no syntax support for monadic code in general, or promises themselves. So this is like JS with promises but without async/await.


I will probably be banned from any Haskell forum after that, but isn't the do notation a async, and the <- a await ?


It's basically that. Example:

    hello.then(a => return op1(a)).then(b => return op2(b)).then(c => console.log(c);

    const a = await hello();
    const b = await op1(a);
    const c = await op2(b);
    console.log(c)
In Haskell (>>= is pronounced "bind", \a -> is like a => in JS):

    hello >>= (\a -> op1 a >>= (\b -> op2 b >>= (\c -> show c)))

    do {
        a <- hello;
        b <- op1 a;
        c <- op2 b;
        show c
    }
There is a slight difference, in that JS promises aren't monads. But the idea is still here.


what do you mean by banned? You got it the other way around, async is a special case of the do notation, and await is the special case of <-


do { a <- m; return r }

Ia desugared to m >>= (\a -> return r)

Think of it m.then(function (a) return r)




Consider applying for YC's Summer 2025 batch! Applications are open till May 13

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: