Gleam Otp – Fault Tolerant Multicore Programs with Actors
Posted2 months agoActive2 months ago
github.comTechstoryHigh profile
supportivepositive
Debate
20/100
GleamErlangBeamFunctional Programming
Key topics
Gleam
Erlang
Beam
Functional Programming
The Gleam OTP project brings fault-tolerant multicore programming with actors to the BEAM ecosystem, sparking discussion among HN users about its potential, comparisons to other languages, and use cases.
Snapshot generated from the HN discussion
Discussion Activity
Very active discussionFirst comment
2h
Peak period
82
Day 1
Avg / period
28
Comment distribution84 data points
Loading chart...
Based on 84 loaded comments
Key moments
- 01Story posted
Oct 19, 2025 at 6:25 PM EDT
2 months ago
Step 01 - 02First comment
Oct 19, 2025 at 8:43 PM EDT
2h after posting
Step 02 - 03Peak activity
82 comments in Day 1
Hottest window of the conversation
Step 03 - 04Latest activity
Oct 31, 2025 at 7:42 AM EDT
2 months ago
Step 04
Generating AI Summary...
Analyzing up to 500 comments to identify key contributors and discussion patterns
ID: 45638588Type: storyLast synced: 11/20/2025, 4:11:17 PM
Want the full context?
Jump to the original sources
Read the primary article or dive into the live Hacker News thread when you're ready.
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.
My original plan was either Elixir or vanilla Erlang depending on which one suits my sensibilities better. Reading about Gleam recently has me super, super excited. That's definitely going to be my path now.
I don't know if Gleam is the best entry into the world of rich types that you find in a language like Haskell--I'm yet to actually build something with it.
What I can tell you is that Haskell is a complete joy to use and it honestly ruins most other programming for me. So as a direction, I cannot recommend it enough, and I'm hoping, for my sake and yours, that Gleam offers a similarly stimulating sandbox.
Just a warning that it will take time to get used to "higher-kinded" types. It's an exercise in head scratching and frustration at first. The reward part arrives when you start thinking in types yourself and you know which ones to reach for and when you find the libraries you want by entering a signature on Hoogle.
I have a F# background, and thought to have read that some constructs I learned to appreciate are not available in Gleam (the one I can think of right now is currying, but I thought there were others).
Also, Gleam otp didn't seem to be a priority.
What's your experience regarding these 2 points?
This idea that static typing is such a massive issue for OTP style servers and messaging is a very persistent myth, to be honest; I've created thin layers on top of OTP for both `purerl` (PureScript compiled to Erlang) and Gleam that end up with both type-safe interfaces (we can only send the right messages to the processes) and are type-safe internally (we can only write the process in a type-safe way based on its state and message types).
• OTP processes communicate via the actor model by sending messages of any type. Each actor is responsible for pattern-matching the incoming message and handling it (or not) based on its type. To implement static typing, you need to know at compile time what type of message an actor can receive, what type it will send back, and how to verify this at compile time.
• OTP's GenServer behaviour uses callbacks that can return various types, depending on runtime conditions. Static typing would require that you predefine all return types for all callbacks, handle type-safe state management, and provide compile-time guarantees when handling these myriad types.
• OTP supervisors manage child processes dynamically, which could be of any type. To implement static typing, you would need to know and define the types of all supervised processes, know how they are going to interact with each other, and implement type-safe restart strategies for each type.
These and other design roadblocks may be why Gleam chose to implement primitives, like statically typed actors, instead of GenServer, GenStage, GenEvent, and other specialized OTP behaviours, full supervisor functionality, DynamicSupervisor, and OTP's Registry, Agent, Task, etc.
OTP and BEAM are Erlang and Elixir's killer features, and have been battle-tested in some of the most demanding environments for decades. I can't see the logic in ditching them or cobbling together a lesser, unproven version of them to gain something as mundane as static typing.
EDIT: I completely missed the word "actor" as the second word in my second sentence, so I added it.
> OTP processes communicate via the actor model by sending messages of any type. Each actor is responsible for pattern-matching the incoming message and handling it (or not) based on its type. To implement static typing, you need to know at compile time what type of message an actor can receive, what type it will send back, and how to verify this at compile time.
This is trivial, your `start` function can simply take a function that says which type of message you can receive. Better yet, you split it up in `handle_cast` (which has a well known set of valid return values, you type that as `incomingCastType -> gen_server.CastReturn`) and deal with the rest with interface functions just as you would in normal Erlang usage (i.e. `get_user_preferences(user_preference_process_pid) -> UserPreferences` at the top level of the server).
Here is an example of a process I threw together having never used Gleam before. The underlying `gen_server` library is my own as well, as well as the FFI code (Erlang code) that backs it. My point with posting this is mostly that all of the parts of the server, i.e. what you define what you define a server, are type safe in the type of way that people claim is somehow hard:
It's not nearly as big of an issue as people make it out to be; most of the expected behaviors are exactly that: `behaviour`s, and they're not nearly as dynamic as people make them seem. Gleam itself maps custom types very cleanly to tagged tuples (`ThingHere("hello")` maps to `{thing_here, <<"hello">>}`, and so on) so there is no real big issue with mapping a lot of the known and useful return types and so on.For normal matters I do believe that your approach works but (start returns the pid of the server, right?) what is it going to happen if something, probably a module written in Elixir or Erlang that wants to prove a point, sends a message of an unsupported type to that pid? I don't think the compiler can prevent that. It's going to crash at runtime or have to handle the unmatched type and return a not implemented sort of error.
It's similar to static typing a JSON API, then receiving an odd message from the server or from the client, because the remote party cannot be controlled.
Yes, `start` is the part you would stick in a supervision tree, essentially. We start the server so that it can be reached later with the interface functions.
> [...] probably a module written in Elixir or Erlang that wants to prove a point, sends a message of an unsupported type to that pid? I don't think the compiler can prevent that. It's going to crash at runtime or have to handle the unmatched type and return a not implemented sort of error.
Yes, this is already the default behavior of a `gen_server` and is fine, IMO. As a general guideline I would advise against trying to fix errors caused by type-unsafe languages; there is no productive (i.e. long-term fruitful) way to fix a fundamentally unsafe interface (Erlang/Elixir code), the best recourse you have is to write as much code you can in the safe one instead.
Erlang, in Gleam code, is essentially a layer where you put the code that does the fundamentals and then you use the foreign function interface (FFI) to tell Gleam that those functions can be called with so and so types, and it does the type checking. This means that once you travel into Erlang code all bets are off. It's really no different to saying that a certain C function can call assembly code.
And the corresponding Erlang code:To be fair, I can't think of anything I care less about than static typing, so please keep that in mind when entertaining my opinion.
I thought I'd share my experience, not trying to convince anyone ; - )
There's not much documentation/resources around OTP in Gleam. When I was playing around with it I often found myself referring to the Elixir docs and then 'translating' that knowledge to Gleam's OTP implementation.
Gleam is still very new so this is totally understandable, and both are great languages so you'll likely have a lot of fun learning either of them.
0 - https://pragprog.com/titles/jaerlang2/programming-erlang-2nd...
I read Programming Erlang later, but it was just for fun, and I knew most things already at that point.
Edit: I do miss static typing, but it's worth it to not have to reinvent the web framework wheels myself.
Some thoughts so far: Installing gleam installed about 50 packages on my system (possibly all erlang/elixir related). But what if I just wanted to transpile to js? Perhaps this is a packaging issue on my system though.
What I really wish is that Lua would be considered as another transpilation target. I think gleam could shine for DSLs in other programs but embedding Lua is 3x easier than embedding a js runtime imo.
In general, I find the nicest thing so far the community. And I want to say that the quality of libraries and resources in the gleam community seems exceptionally high. It reminds me of the rust community insofar as that for most hard problems someone smarter than me has already worked on it and published a good solution (e.g. lustre, squirrel). Then I would say you can also expect a lot of creativity and experimentation, the things that may fly under the radar in larger language ecosystems stand out here due to the growing and welcoming community.
Gleam is only officially distributed via the releases on GitHub [1] so if you pick up Gleam from a package manager that's always maintained by someone else. I think most of those community distributions do include Erlang and bits which is probably what pulled in all those extra packages!
[1]: https://github.com/gleam-lang/gleam/releases/tag/v1.13.0
https://www.phoenixframework.org/
https://www.amazon.com/Programming-Phoenix-Productive-Reliab...
If you don't run away from a framework intro, then dive into the details of the OTP:
https://www.amazon.com/Designing-Elixir-Systems-OTP-Self-hea...
https://www.amazon.com/Elixir-Action-Third-Sa%C5%A1a-Juric/d...
The only foot-gun I would initially avoid, is a fussy fault-tolerant multi-host cluster deployment. Check out RabbitMQ package maintainers, as those guys certainly offer a fantastic resource for students ( https://www.rabbitmq.com/docs/which-erlang .)
Best of luck =3
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.
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.
can you please explain how there is risk of deadlock here ? thanks !
This is rarely a problem in practice however.
but this whole thread started with deadlock on remote calls, and i was curious about how that could be with async calls.
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 are you talking about 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?
> The BEAM is popular.
At this point I'd say it's more popular than ZIO, an effects/io/concurrency library basically implementing its own language and threading model on top of Scala
I see a lot of people that dislike Erlang that run into this or are afraid of running into it.
Receive with timeout is certainly a primative to handle this, and there's things like process aliases so that late responses can be more easily ignored. But, there's nothing structural preventing it. It's just that when you do it, it hurts, so hopefully you stop doing it.
But when you're in a situation whete you've setup circular messaging, it's very easy to identify. You see a process (A) that has is making no progress and/or has a large mailbox. You see the stack trace shows it is messaging process B, you inspect process B to see why it didn't respond and see it is messaging process A and there you go. Or you look at process A's mailbox and see that it has requests related to the request it's already trying to fill.
> The actor model doesn't really offer any benefit over other models while bringing significant downsides.
I find the actor model makes many types of request/response for persistent state to become very simple. A process owns the state, and application level read/write messages are sent to that process. The mailbox serializes access to provide an ordering and to make changes atomic. You certainly have choose your messages; if you have a counter and do a get and a set message, you will lose updates; you need to do increment/decrement/add kind of messages; same thing for larger/more complex datastructures. Linked datastructures are still tricky.
It's also super nice for request/response connection handling. Just write straight line code that reads from the connection when it needs a request and writes to it when the response has been computed. When you need to be asynchronous, it's more complex, but not so terrible. The process becomes a loop waiting for a fully formed request from the client (which then gets sent off to a request processor) or a reply from a processor (which then gets sent to the client) ... Java's Loom green threads will likely socialize this further than Erlang has.
This kind of feeds into your other question, but IMHO, Actors let you focus on a process owning state and the messages it receives and how they change the state. You don't need to model for the computer where those messages come from, that's emergent system behavior. BEAM doesn't need to know, and doesn't ask, in part because as a changable distributed system, there's not really a way to know. What sends what to who is essential complexity of the system, but the system doesn't need to know about it. If you had a full accounting of how messages are sent, then you potentially find message loops, but ... it's easier to just find and fix any loops at runtime. (hotloading code can make it very low cost to deploy changes, which makes it more reasonable to spend less effort finding issues before release)
> Plus, there are major downsides to using an unpopular platform like Erlang/BEAM.
Sure, you have to do more work yourself. On the otherhand, relying on the work of others isn't always less work than doing it yourself. There are at least a few Erlang consulting shops you can go to if you really need something and are unwilling or unable to do it. You can't find a lot of answers on stackexchange, but you also don't find a lot of wrong or outdated answers either.
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).
The BEAM is, in fact, the perfect platform for the exact use case you describe - fault-tolerant, distributed/concurrent systems.
This video from 9 years ago... 1 million people in 1 chatroom, on a server with 40+ cores and 128GB of memory, one single physical server, it used up about 30 GB of memory. Show me a language that can do this efficiently. In under a second it went to a million different connections. The cores didn't spin up to grind hardcore, none of that. Just efficient lightweight processes running. This isn't even on "raw Erlang" its on Elixir -> BEAM which is impressive on its own in my eyes. I would love to see equivalent videos on similar hardware today in other languages. ;) Please by all mean, show me your JVM application that can handle this.
I also love that he thinks all those languages that are a decade (and decades) younger than the BEAM which has been in production since 1992 are more production ready and tested. I don't think he realizes the origin story to Erlang whatsoever, it was 100% built to be used in production for a major telecoms provider.
https://youtu.be/N4Duii6Yog0
Translation: I think I'm better off using needlessly complex overengineered monstrosity that takes insane effort to do even the simplest of things, and that still gives me no actual guarantees, than a simple system that has been battle tested over decades of actual hardcore industrial use.
In fact, functional effects provide another elegant solution to problems requiring structured concurrency. You might like it, you might not. But there are many engineers in the Scala community who were bitten by the exact issues of actor deadlocking / difficulty of debugging compared to functional effects that refactored enterprise codebases from Akka (Scala actors) to functional effects.
"A simple system that has been battle tested over decades of actual hardcore industrial use". The JVM certainly is not far from that.
There's nothing elegant about ZIO.
> But there are many engineers in the Scala community who were bitten by the exact issues of actor deadlocking / difficulty of debugging compared to functional effects
Because you can't truly retrofit a proper actor model onto a runtime that doesn't support it, and doesn't have proper tools for them.
So they reach for third-party libs that try to implement half of Erlang, often poorly, and get understandably bitten by issues.
> "A simple system that has been battle tested over decades of actual hardcore industrial use". The JVM certainly is not far from that.
The JVM isn't built around built-in support for concurrency and distributed computing.
Erlang's BEAM is. And its built-in primitives and standard library has been around for over three decades.
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.
I'm sorry, but your multicore computer (and most single core computers, too) are a distributed system, regardless of if you acknowledge it.
Shared memory concurrency use implicit communicatiom between threads, but it's still communicating processes, you just don't have the same level of control and isolation. That said, of course there are situations where you can have better results with shared memory and there are situations where message passing is better. You can do shared memory in BEAM with ets, and if that doesn't meet your needs, with NIFs ... you likely lose out on some isolation, etc, and it won't always be a good fit.
> Plus the JVM software ecosystem and runtime observability / battle-testedness is far better than BEAM.
Maybe battle tested (although, I've certainly battle tested BEAM more than I've tested JVM; but I'll grant that many more people have done battle with the JVM)... but BEAM has quite extensive observability. You can see all sorts of stuff about every process, you can add tracing at run time. You can change the code at run time if you need to add more stuff; and runtime code changing is just in the normal setup, it's not exotic (although many people eschew it)
This is, quite literally, Erlang's job. It's one job. And it is proven *excellent* at it.
> purerl is a PureScript backend targetting Erlang source.
The backend is not Erlang. It’s a Purescriot backend.
https://github.com/purerl/purerl
This approach is notably not making proper or really any use of OTP features, but it was an extremely easy way to adopt a safe, fast, functional language for number-crunching workflows while continuing to lean on Rails for everything else its great at: web interfaces, HTTP APIs, etc.
Rails is basically the configuration tool for the various inputs of a job, and job is passed to Gleam via Redis as an atomic set of config inputs to be used when processing a dataset (usually big CSV files streamed from object storage). We use a very thin Elixir wrapper to do all network and file IO etc, Gleam modules are pure business logic called from Elixir.
Some day soon, I'm going to try and write up a longer technical article about this approach... it comes up surprisingly often in HN conversations.
If we encounter row-level errors in a batch, we log those alongside the outputs. There's nothing particularly intrinsic about out usage of Gleam that prevents the workers from crashing during processing, its all about having error handling set up within the job itself to avoid killing the process or pod running it.
It is an excellent general purpose language that succeeds in a lot of domains.
Check out my conversation from Developer Voices for more info
https://youtu.be/_MwXbHADT-A?si=2lRqjwAY9dsODyhW
You could have chosen not to deviate the discourse.
Plaster? Are you referring to this singular line of text way down on their main page? If this statement precludes you from using the project I would imagine it is working as intended.
- squirrel: https://github.com/giacomocavalieri/squirrel
- Lustre: https://github.com/lustre-labs/lustre
- Wisp: https://github.com/gleam-wisp/wisp
https://www.youtube.com/watch?v=TbCm-zR7qZ0
Don't pay too much mind on the ruby-like syntax. I also prefer the syntax of Gleam, but Elixir really isn't bad when you get used to it.
JSON handling in Gleam was also something that annoyed me and made be scrap a hobby project and go with Elixir instead. I wish they had a proper macro system so you could derive json encoding/decoding like you do in Rust...
The BEAM's grown up along with Erlang and so the culture and optimisations are built up all around function application, list processing, recursion, and pattern matching etc.
https://github.com/llaisdy/beam_languages is a decent list of the diverse languages that have been implemented on it, but nothing quite like 'regular for and while loops'.
The BEAM itself is a plain aul register machine though, so it could be done!
https://www.erlang.org/blog/a-brief-beam-primer/
1 more comments available on Hacker News