4 ms·
I agree with the article but not with the editorialization in the title. In the last 2 years I've mostly used Elixir and JavaScript. In Elixir, generally all f
by skrebbel 9y ago
I agree with the article but not with the editorialization in the title.
In the last 2 years I've mostly used Elixir and JavaScript. In Elixir, generally all functions are synchronous. In JavaScript, there's the typical two-color functions that the author describes: synchronous and asynchronous.
Elixir is nicer by far for a lot of parallel programming. Our backend can handle large loads on a single VM simply because of this.
However, the world is asynchronous. It's very nice that Elixir abstracts it away, but sometimes that's not what you want and then Elixir makes things significantly harder than JavaScript.
For example, imagine that my backend gets many parallel requests for mildly different resources. Each resource needs to query some other system (some 3rd party API, a DB, whatever), but it turns out that many of them will do exactly the same request to that other system even though the user requests they're handling are all pretty much unique. A simple solution is to cache the requests every second/minute/whatever and combine them all. This is nontrivial because I want to cache the running requests, not the responses. If there's one request running, I don't want to launch an identical request.
In Elixir, I have to carefully design a GenServer to make this work properly. Or maybe use a library such as the fantastic `memoize`[0] which is great for this, and can even do it without spawning any additional processes at all. But in any way, I have to add complexity - either complexity that I write or complexity that I import. `memoize`'s core caching code is amazing, but not trivial[1].
In JavaScript, I can just cache a bunch of promises in a Map or an object. If the promise exists and hasn't expired, don't do a new request. Else, do the request and cache the promise. Then, in either case, await the promise and use the result. It's 10 lines of boring code.
I still like Elixir better for backends and I'm happy that our backend isn't Node. But I'm writing this to underline that explicit support for asynchronicity can be a feature too.
[0] https://github.com/melpon/memoize https://github.com/melpon/memoize
[1] https://github.com/melpon/memoize/blob/master/lib/memoize/cache.ex#L94 https://github.com/melpon/memoize/blob/master/lib/memoize/ca...
- mercer 9y agoI'm very much a beginner at this, so apologies if I'm missing something obvious, but I find this an interesting scenario to think about (so even just to learn how you would do it in Elixir would be nice). Why not track the fetching processes in another managing process (or ETS)? Whenever a new request comes in, you check if it there's a process already busy fetching that particular resource. If there is, ask it to send the results back to this request process too, or use whatever storage that keeps this data to satisfy both requests. In practice I'd probably be using some kind of managing process anyways, to deal with queuing multiple requests to 3rd party provider (because rate limiting and whatnot), and I'd perhaps also have some storage and 'inform requesting party of result' logic/process in place too, so I'd not be adding too much extra complexity. Is this a valid approach to begin with? Or does this expose me as very much an Elixir/OTP beginner :)? And if the approach works, what complexity am I missing compared to keeping track of promises in a map? Or am I overestimating how much complexity you're talking about? I had my first taste of doing some 'real' stuff exactly with caching responses and dealing with rate limiting and whatnot, and I found that to be quite nice to do in Elixir, but this particular 'problem' strikes me as one I'll probably run into fairly soon.
- Piezoid 9y agoFacebook built Haxl[0], a library in Haskell, precisely for this use case: batching, caching and paralyzing requests against external sources. [0] https://github.com/facebook/Haxl https://github.com/facebook/Haxl
- notacoward 9y agoI think you meant "parallelizing" but "paralyzing" often seems to be the result. ;)
- he0001 9y ago> However, the world is asynchronous. Can you give me examples of sources that explains this concept? I really don’t think the world is asynchronous, because if it were the Big Bang could happen now. Aren’t you mixing the word ‘asynchronously’ with ‘independently’?
- Someone 9y agoHave you been staring ahead mindlessly for the past hour, blocked while waiting for an answer, or did you do something else? I’m not the one making the “the world is asynchronous” claim, but I think this is what (s)he ment: in the real world, there is no “make a request and wait for an answer, doing nothing at all until you get an answer, even if one never comes”. That need not be true, though. One could model the human brain as a blackboard with N cores, each running a single process (one for breathing, one for your heart rate, etc), and one of those cores blocking forever on such a question, with a “OOT killer” killing a low priority thread when needed. I wonder how one would go about falsifying such a model…
- he0001 9y agoWaiting for your answer pretty much was like that, and yes I did a lot of completley unrelated stuff. I don’t think computers model the “real world” pretty well. Though they can model a lot of other things very well. The real world isn’t asynchronous, it’s continuous with something seemingly unrelated. If this world were asynchronous, there would be an possibility that I would have given this answer before you gave me an question. Where are the synchronizing primitives in the world making sure that this never happens?
- inyourtenement 9y agoCould you define synchronous and asynchronous as you understand them? Because I don't think your definitions match the rest of the people in this thread.
- he0001 9y ago