3 ms·
This article is exceptionally well written. The author did a good job at making the subject approachable. I disagree with this phrase: > By forcing the mutati
by gr4vityWall 2y ago
This article is exceptionally well written. The author did a good job at making the subject approachable.
I disagree with this phrase:
> By forcing the mutation of state to be serialized through a process’s mailbox, and limiting the observation of mutating state to calling functions, our programs are more understandable
My experience is quite the opposite - that's a mental model for programs that goes against how most people I know reason about code.
The examples in Elixir all looked more complicated for me to understand, generally, although I understand the value brought by that model. The cognitive load seemed fairly higher as well.
- azeirah 2y ago> My experience is quite the opposite - that's a mental model for programs that goes against how most people I know reason about code. > The examples in Elixir all looked more complicated for me to understand, generally, although I understand the value brought that model. The cognitive load seemed fairly higher as well. Of course it's not as understandable to someone who's not used to it. When I read articles about a different paradigm, I assume that "better" means "with equal experience as you have in your own paradigm" So if someone says "this convoluted mess of a haskell program is better than js", I will say "yes, if you spent 100 hours in Haskell prior to reading this".
- gr4vityWall 2y ago> Of course it's not as understandable to someone who's not used to it. Sorry, I didn't mean to imply otherwise. Perhaps the original quote should make what you said ("with equal experience as you have in your own paradigm") explicit. I do believe that the paradigm proposed in the article has a much higher learning curve, and expect it to not be adopted often.
- cpill 2y agoHey, and hence the rampant adoption of Haskell :P
- rkangel 2y ago> The examples in Elixir all looked more complicated for me to understand, generally, although I understand the value brought that model. The cognitive load seemed fairly higher as well. I can see that might be the case with simple examples, but with more complex systems I find the cognitive load to be much lower. The Elixir/Erlang approach naturally results in code where you can do local reasoning - you can understand each bit of the code independently because they run in a decoupled way. You don't need to read 3 modules and synthesise them together in your head. Similarly the behaviour of one bit of code is much less likely to affect the behaviour of other code unexpectedly.
- gr4vityWall 2y agoQuestion: do you know any of such more complex systems which are Free Software, so that I could take a look? Sounds like it would be a fun learning experience.
- rkangel 2y agoI'd love to point you at some code from work but it's all closed source unfortunately. One example that gets used is https://github.com/hexpm/hex https://github.com/hexpm/hex which is the code behind the Elixir package management website. It's more a good example of a Phoenix app than it is general OTP stuff, but there are some GenServers in there.
- gr4vityWall 2y agoThank you, I'll check it out. :)
- toast0 2y agoI think understandable can mean different things here. If we're looking at a state dump and your data is in a weird place, I'd want to understand how the data got into that weird place. In a model with state owned by a process, and changes coming in through the mailbox, I know that all of the state changes happened through processing of incoming messages, one at a time. If I'm lucky, I might have the list of messages that came in, and be able to run them one a time, but if not, I'll just have to kind of guess how it happened, but there's probably only a limited number of message shapes that are processed, so it's not too hard. There's a further question of how those messages came to be in the process's mailbox, of course. In a model with shared memory mutability, it can be difficult to understand how an object was mutated across the whole program. Especially if you have errors in concurrency handling and updates were only partially applied. There's certainly a learning curve, but I've found that once you've passed the learning curve, the serialized process mailbox model makes a lot of understanding simpler. Individual actors are often quite straight forward (if the application domain allows!), and then the search for understanding focuses on emergent behavior. There's also a natural push towards organizing state into sensible processes; if you can organize state so there is clear and independent ownership of something by a single actor, it becomes obvious to do so; it's hard to put this into words, but the goal is to have an actor that can process messages on that piece of state without needing to send sub-requests to other actors; that's not always possible, sometimes you really do need sub-requests and the complexity that comes with it.
- gr4vityWall 2y agoThat's an interesting point of view, thanks for taking the time to write that. I wonder how much that learning curve is worth it. It reminds me of the Effect library for TypeScript, in that regard, but Elixir looks more readable to me in comparison.
- toast0 2y ago> I wonder how much that learning curve is worth it. It really depends on how well your application fits the model. If it's a stretch to apply the model and you're already comfortable with something else, it might not be worth it. But if you're building a chat server, or something similar, I think it's very worthwhile. I think it can be pretty useful if you have a lot of complex state per user, in general, as your frontend process could send process inbound requests into messages into a mailbox where each user would only be processed by a single actor (each user doesn't need their own actor, you could hash users in some way to determine which actor handles a given user). Then you have a overall concurrent system, but each user's complex state is managed in a single threaded manner. I think process/actor per connection is a superior mental model to explicit event loops for something like a large HTTP server, but unless the server is performing complex work within it, I would assume without testing that the explicit event loop would win on performance. I think it would be more fun to build a large HTTP server in Erlang than with c and kqueue, but nginx and lighttpd are already written, and quite fast. When I needed to do a TCP proxy for million + connections, I took HAProxy and optimized it for my environment, rather than writing a new one in Erlang; that was still fun, but maybe different fun; and being able to handle tens of thousands of connections with minutes of configuration work was a better organization choice than having fun building a proxy from scratch. :)
- jimbokun 2y ago> that's a mental model for programs that goes against how most people I know reason about code. But the mental model most of us have for reasoning about code in environments with concurrent execution is simply wrong. So the Elixir model is more understandable, if you want a correct understanding of what your code will do when you run it.
- gr4vityWall 2y ago> the mental model most of us have for reasoning about code in environments with concurrent execution is simply wrong. Could you elaborate on that?
- jimbokun 2y agoIt is very difficult to write thread based code with no bugs.
- necovek 2y agoI don't think that's true: it's pretty easy, really (basically, adopt the message passing/directed-acyclic-graph structure in your threads). What is true is that it's too easy to write threads-based code with bugs, and therein lies the problem.
- hinkley 2y agoPeople seem to do okay figuring out concurrency in programming languages with global interpreter locks. At some point you know actions are happening serially, so you have to worry less about having to update three pieces of state to finish a task because unless you start a side quest in the middle of the calculation, any other code will either see the Before or After state, not any of the 2 intermediate states.