5 ms·
> maybe it was a mistake to adopt an async paradigm from functional languages > I think Rust was right to pick Scala-style async I'm confused by this assertio
by stormbrew 6y ago
> maybe it was a mistake to adopt an async paradigm from functional languages
> I think Rust was right to pick Scala-style async
I'm confused by this assertion. I'm more aware of procedural language origins of syntactic async/await than functional? The scala proposal in 2016 for async/await even cites C#'s design (which came in C# 5.0 in 2012) as an inspiration[1].
From there, it appears python and typescript added their equivalents in 2015 [2].
If anything, async-await feels like an extremely non-functional thing to begin with, in the sense that in a functional language it should generally be easier to treat the execution model itself as abstracted away.
[1] https://docs.scala-lang.org/sips/async.html https://docs.scala-lang.org/sips/async.html
[2] https://en.wikipedia.org/wiki/Async/await https://en.wikipedia.org/wiki/Async/await
- dwohnitmok 6y ago> If anything, async-await feels like an extremely non-functional thing to begin with Futures/promises (they mean different things in different languages), like many other things, form monads. In fact async-await is a specialization of various monad syntactic sugars that try to eliminate long callback chains that commonly affect many different sorts of monads. Hence things like Haskell's do-notation are direct precursors to async-await (some libraries such as Scala's monadless https://github.com/monadless/monadless https://github.com/monadless/monadless make it even more explicit, there lift and unlift are exactly generalized versions of async and await). To see how async-await might be generalized, one could turn to various other specializations of the same syntax, e.g. an async that denotes a random variable and an await that draws once from the random variable. To see the correspondence with a flatMap method (which is the main component of a monad), it's enough to look at the equivalent callback-heavy code and see that it looks something like Future(5) .flatMap(x -> doSomethingFutureyWithX(x) .flatMap(lookItsAnotherCallback) )
- stormbrew 6y agoI'm not clear on if this is supposed to be disagreement or elaboration or education. The fact that in a language like Haskell, you can perform something like async-await with futures (which are absolutely a kind of monad) in a natural way is precisely what I had in mind with what you quoted. Regardless, the specific heritage of async-await syntax seems rooted in procedural languages (that do borrow much else as well from functional languages, yet are still not functional in any meaningful sense) like C# and python. They are absolutely an attempt to bring some of the power of something like monadic application (including do notation) into a procedural environment as an alternative to threads (green or otherwise), which hide the execution state machine completely.
- runT1ME 6y agoYou don't need async/await to do monadic comprehension in Scala, it's built into the language from the very beginning with `for`. This was inspired by do notation, which came about ~1998.
- deleted 6y ago[deleted]
- dwohnitmok 6y ago> the specific heritage of async-await syntax seems rooted in procedural languages I don't think so. Async-await in both syntax and semantics is pretty firmly rooted in the FP tradition. For semantics, the original implementation in C#, and as far as I know most of its successors, is to take imperative-looking code and transform it into CPS-ed code. That's a classic FP transformation (indeed it's one of the paradigmatic examples of first-class functions/higher-order functions) and one of the most popular methods of desugaring imperative-looking code in a functional context. For syntax, the idea of hiding all that CPS behind an imperative-looking syntax sugar is the whole reason why do-notation and its descendants exist. (Indeed, there's an even deeper connection there specifically around CPS and monads: https://www.schoolofhaskell.com/school/to-infinity-and-beyond/pick-of-the-week/the-mother-of-all-monads https://www.schoolofhaskell.com/school/to-infinity-and-beyon...) But my point was simply that there's a pretty straight line from CPS, monads, and do-notation to async-await and so I think it's pretty fair to say that async-await is rooted in the FP tradition.
- deleted 6y ago[deleted]
- lmm 6y ago> I'm more aware of procedural language origins of syntactic async/await than functional? The scala proposal in 2016 for async/await even cites C#'s design (which came in C# 5.0 in 2012) as an inspiration[1]. The C# version comes from F# (2007) which was in turn inspired by the "Poor Man's Concurrency Monad" implementation for Haskell (1999) (in turn inspired by Concurrent Haskell and, ultimately, Concurrent ML). It's very much a functional lineage.
- stormbrew 6y agoI wasn't aware of the F# heritage, that's interesting. I'm curious why the scala proposal wouldn't cite it. Especially surprising since scala's at least superficially looks more like F#'s than it does like C#'s. I don't dispute (as I have had to say repeatedly in other branches of this) that the roots of futures as a concept are in functional programming, but the path I'm saying I see here is effectively: - haskell/monads |- do-notation |- monadic futures |- (this is new to me) F# appears to have added 'types' of do blocks, including a specifically async one? \-> async-await as a sort of re-integration of quasi-monadic evaluation into procedural languages like C#, python, and eventually javascript and rust. So what's weird to me is drawing a direct line between scala and rust here when the relevant precedent seems to be procedural languages distilling a thing from functional languages into a less flexible syntactic-sugar mechanism we now usually call async-await. Scala seems like a footnote here, where if you want to claim its descent from functional languages you would go farther back.
- lmm 6y agoI don't see async/await (at least when built on top of futures/promises) as a procedural thing - the parts of C# where it's used are the least procedural parts of C#, and Python has always been multi-paradigm. I'd say it's mainly a way of doing monadic futures in languages that don't have monads (mainly because of lacking HKT) - hence why F# adopted it first, and then it made its way into functional-friendly languages that either didn't have HKT, or in Scala's case found it to be a useful shortcut anyway. "Functional" is a spectrum rather than a binary, but I don't think it's right to see async/await as being imperative any more than having map/reduce/filter in C#/Python/Javascript makes them an imperative thing. (I would agree that Haskell and Scala, with true monads, are more functional than C#/Python/Javascript - but I'd say that having async/await means C#/Python/Javascript are closer to Haskell/Scala than similar languages that don't have async/await; async/await are making them more functional, not less). As for why I mentioned Scala specifically, I understand that Rust's Futures are directly based on Scala's. I had assumed this would apply to async/await as well, but it sounds like apparently not? In any case there's not a huge difference between the Scala/C# versions of the concept AFAICS.