News for hackers. Stuff that matters.
Re:Elixir GenServer Explained by ammanley on Tuesday April 27, 2021 @09:30PM (#26964310)
Thank you so much for sharing this. I've not yet finished a decade in the industry, nor ever laid an eye on Elixir before, and this made sense pretty quickly. Great job explaining it all!

On a side note, its pretty amazing to see the impact on readability and understanding that native language features have on something like async. I've worked with similar systems to the example in the past in JS/Go/etc, and while you can definitely do the same stuff presented here, it tends to be a far messier callback-hell ime. For lack of better explanation, this Elixir code just "flows" better to my eye, which I know is a completely subjective and non-helpful unsolicited piece of info. Thanks again.

Re:Elixir GenServer Explained by lostcolony on Tuesday April 27, 2021 @11:05PM (#26965052)
It flows better because it is writing synchronous code. The code inside of every process in Erlang is synchronous.

You achieve concurrency by spinning up processes (spawning functions). This isn't that different to firing an async function in Javascript, or starting a goroutine, etc.

And you communicate between the processes by sending async messages.

Those are the primitives. Asynchronously send messages between processes that are synchronously doing stuff. Go is actually not -that- different, except sending messages is synchronous, too, unless you set a buffer on the channel (and then you have to size it). Though I agree, because Erlang/Elixir keep it simpler than that, and are dynamically typed, it's a lot cleaner to write.

Re:Elixir GenServer Explained by fn1 on Wednesday April 28, 2021 @02:31AM (#26966361)
The formal framework for this is called CSP ("Communicating sequential processes"). I discovered this 15 years ago, and once you understand it, you will never have problems in multithreading again.

https://arild.github.io/csp-presentation/#3

Re:Elixir GenServer Explained by lostcolony on Wednesday April 28, 2021 @10:19AM (#26969652)
Not exactly. CSP is more what Golang adheres to (synchronous communication). The Actor Model predates CSP. And understanding it you'll still have problems...they're just different problems. You either won't be able to use a language with message passing as a concurrency primitive (i.e., almost all popular languages other than Go at this point), or you will, and now you'll have the time to spend on new types of problems (i.e., you can still create race conditions and deadlocks, and, in Erlang at least, now you get to start asking yourself what happens when things go wrong to a level you never would have with any other language).
Re:Elixir GenServer Explained by OkayPhysicist on Thursday April 29, 2021 @08:19AM (#26980194)
The nice thing about the cleanly independent processes is that by and large, "let that part crash, and keep going" is a perfectly adequate answer to most process states outside the pretty path, which is a real saving grace when working with wildly concurrent systems.
Re:Elixir GenServer Explained by lostcolony on Thursday April 29, 2021 @10:48AM (#26981863)
Oh, certainly.

On this particular distinction, in fact, it's one of the benefits the actor model has over CSP. In the actor model (well, Erlang's take on it, but that's one that has been adopted elsewhere such as in Akka), I care about "if this fails, who should be affected by it". I.e., by default, it's just this process, and with a supervisor it'll restart. How do I get it restarting into a good state? If it goes down, who else needs to go down (to keep state consistent)? That's mostly all I have to ask myself; I don't have to ask what all the ways it could go down are (slight caveat there; you still want circuit breakers around external resources).

With CSP, I have to ask "if this goes down, who else IS affected by it". Not who should be, and be intentional about linking them, but determine who accidentally is affected by it. This is everyone who might be sending or receiving across a channel (to borrow Go parlance) this process has access to. Which breaks the abstraction; from the perspective of the current process I shouldn't have to know who is sending or receiving across a channel, but the reality is I -do-. And that's no better than the error model I have in other languages; I have to understand every part of my application to know what may or may not be affected by this one part going down. Go gets 'around' this by just making it so any uncaught exception (err, unrecovered panic) crashes the whole application, which is certainly one way to do it.

Re:Elixir GenServer Explained by dnautics on Tuesday April 27, 2021 @10:18PM (#26964699)
Oh that's so funny, because I'm an elixir user and we all claim that genserver (internal) organization is terrible.

But yes, it is way better than callback hell... It just could be better still.

Re:Elixir GenServer Explained by HH_GU on Wednesday April 28, 2021 @02:31AM (#26966357)
not genserver, what will you use?
Re:Elixir GenServer Explained by dnautics on Wednesday April 28, 2021 @02:40AM (#26966416)
There isn't anything. Dave thomas proposed a better way of organizing genservers, but I can't get behind it because his implementation is too magical and clever, but his idea for what the api should look like in the big picture is reasonable... I'm also generally in favor having everyone speak the same idioms, so it would take a very good implementation to get me off of genserver.

That said you shouldn't write genservers if you can help it. Using the frameworks genservers Is the best choice, Task is a better choice for most cases.

Re:Elixir GenServer Explained by pandemic_region on Wednesday April 28, 2021 @03:32AM (#26966740)
I completely relate to the "code that just flows" statement. Cognitive overhead when dealing with code is a thing. I've been trying to explain this to a bunch of junior devs at every opportunity i get but all I get is a blank stare. Seems that the more senior and especially bittervet developers are a lot more sensitive to this. Thanks for sharing!