PureScript is a mature functional programming language with an Erlang back end, if you want another statically typed alternative for BEAM. It is basically a dialect of Haskell with strict evaluation and row polymorphism.
IMHO the actor model is great until you need to share something across processes, then you have a distributed computing problem inside your program.
For developing fault tolerant multicore programs I think I'm better off using a functional effects system with software transactional memory like Scala/ZIO than Gleam/OTP. I can still use the actor model where appropriate. Plus the JVM software ecosystem and runtime observability / battle-testedness is far better than BEAM.
This is a wild take. It's one thing to criticise the BEAM for things it's bad at, it's another to criticise it for the thing it absolutely excels at.
The BEAM is built around passing immutable messages between cheap green threads, watched over by resilient supervisor trees. This means no data races (immutability), no hung threads (supervisor trees), no boilerplate (all of this is built in). The performance is surprisingly good, since mandatory immutability permits reference-based optimisations (copying, passing, etc).
It's the perfect platform for the exact use case you describe - fault-tolerant, distributed/concurrent systems.
>IMHO the actor model is great until you need to share something across processes
Incidentally that's what actors are designed for, passing data and being able to mutate their state without use of explicit synchronisation. You either copy or transfer ownership of data from one actor to the next via message passing. Actual sharing should be only done if the data in question is globally immutable.
In Elixir/Gleam/OTP.., the entire program is a collection of progresses which are isolated from each other. Even if you don’t implement the actor pattern, passing state between processes and coordinating is a solved problem. We have primitives like tasks, agents, GenServer, Supervisors etc.
Whenever you message another process and need a reply there is a risk of deadlock. I didn't find any primtives in OTP for handling this, you have to structure your actor interaction to avoid it. You can't just have a little bit of shared memory.
The actor model doesn't really offer any benefit over other models while bringing significant downsides. Plus, there are major downsides to using an unpopular platform like Erlang/BEAM.
> Whenever you message another process and need a reply there is a risk of deadlock.
There are edge cases, sure, but I have yet to encounter a deadlock after 7 years of professional work with Elixir.
> I didn't find any primtives in OTP for handling this
See `GenServer.call/2`. This covers 99% of call/return patters in distributed systems. I take it you haven’t written much (any?) Elixir because you would have found this function.
> The actor model doesn't really offer any benefit over other models while bringing significant downsides.
Actors are a way better abstraction for pretty much any application-level code I can think of. I say this having written Go, Rust, and Elixir. What downsides specifically?
> Plus, there are major downsides to using an unpopular platform like Erlang/BEAM.
The BEAM is popular. At least 3 different serious languages use it. What down sites are you waving your hands up here?
Beam languages have a different concurrency model than what you’re used to in JVM world. I suggest that you try some of them in a real project (even without actors).
Risk of deadlock is real if you have processes calling each-other in a cyclic way. e.g. process A sends GenServer call to process B, that then sends a GenServer call to process A to in order to handle the original call. However, process A is busy waiting on B to reply to it's initial call.
I just started a small project using gleam / lustre, and so far I’m loving it.
Worth trying if you’re on the fence, especially if you’re into static types, no nulls, functional, ML type languages. Plus beam of course.
PureScript is a mature functional programming language with an Erlang back end, if you want another statically typed alternative for BEAM. It is basically a dialect of Haskell with strict evaluation and row polymorphism.
The website and gh repo say it compiles to JS. Where did you learn that it has an Erlang backend?
https://github.com/purescript/documentation/blob/master/ecos...
Isn’t it the opposite of what the GP stated:
> purerl is a PureScript backend targetting Erlang source.
The backend is not Erlang. It’s a Purescriot backend.
https://github.com/purerl/purerl
I think that’s just confusing wording. It sounds like it’s a backend for the Purescript compiler that generates Erlang.
[dead]
Very cool! Looking forward to trying it out.
IMHO the actor model is great until you need to share something across processes, then you have a distributed computing problem inside your program.
For developing fault tolerant multicore programs I think I'm better off using a functional effects system with software transactional memory like Scala/ZIO than Gleam/OTP. I can still use the actor model where appropriate. Plus the JVM software ecosystem and runtime observability / battle-testedness is far better than BEAM.
This is a wild take. It's one thing to criticise the BEAM for things it's bad at, it's another to criticise it for the thing it absolutely excels at.
The BEAM is built around passing immutable messages between cheap green threads, watched over by resilient supervisor trees. This means no data races (immutability), no hung threads (supervisor trees), no boilerplate (all of this is built in). The performance is surprisingly good, since mandatory immutability permits reference-based optimisations (copying, passing, etc).
It's the perfect platform for the exact use case you describe - fault-tolerant, distributed/concurrent systems.
>IMHO the actor model is great until you need to share something across processes
Incidentally that's what actors are designed for, passing data and being able to mutate their state without use of explicit synchronisation. You either copy or transfer ownership of data from one actor to the next via message passing. Actual sharing should be only done if the data in question is globally immutable.
In Elixir/Gleam/OTP.., the entire program is a collection of progresses which are isolated from each other. Even if you don’t implement the actor pattern, passing state between processes and coordinating is a solved problem. We have primitives like tasks, agents, GenServer, Supervisors etc.
Whenever you message another process and need a reply there is a risk of deadlock. I didn't find any primtives in OTP for handling this, you have to structure your actor interaction to avoid it. You can't just have a little bit of shared memory.
The actor model doesn't really offer any benefit over other models while bringing significant downsides. Plus, there are major downsides to using an unpopular platform like Erlang/BEAM.
> Whenever you message another process and need a reply there is a risk of deadlock.
There are edge cases, sure, but I have yet to encounter a deadlock after 7 years of professional work with Elixir.
> I didn't find any primtives in OTP for handling this
See `GenServer.call/2`. This covers 99% of call/return patters in distributed systems. I take it you haven’t written much (any?) Elixir because you would have found this function.
> The actor model doesn't really offer any benefit over other models while bringing significant downsides.
Actors are a way better abstraction for pretty much any application-level code I can think of. I say this having written Go, Rust, and Elixir. What downsides specifically?
> Plus, there are major downsides to using an unpopular platform like Erlang/BEAM.
The BEAM is popular. At least 3 different serious languages use it. What down sites are you waving your hands up here?
OTP typically handles this with timeouts and then restarts when timeouts occur. Not to say it can't happen, but there are strategies.
sometimes people think they know better and want to reinvent the wheel
Beam languages have a different concurrency model than what you’re used to in JVM world. I suggest that you try some of them in a real project (even without actors).
```
```can you please explain how there is risk of deadlock here ? thanks !
Risk of deadlock is real if you have processes calling each-other in a cyclic way. e.g. process A sends GenServer call to process B, that then sends a GenServer call to process A to in order to handle the original call. However, process A is busy waiting on B to reply to it's initial call.
This is rarely a problem in practice however.
you are not blocked on response right ?
receive takes a timeout. A would crash/hit the timeout and deal with the problem.
Unpopular as opposed to what, Scala/Zio?
Avoiding exactly that is why erlang gives you genserver.
[dead]