# glixir 🌟
[](https://hex.pm/packages/glixir)
[](https://hexdocs.pm/glixir/)
**Seamless OTP interop between Gleam and Elixir/Erlang**
Bridge the gap between Gleam's type safety and the battle-tested OTP ecosystem. Use GenServers, Supervisors, Agents, and more from Gleam with confidence.
## Features
- ✅ **GenServer** - Type-safe calls to Elixir GenServers
- ✅ **DynamicSupervisor** - Runtime process supervision and management
- ✅ **Agent** - Simple state management with async operations
- 🚧 **Registry** - Process registration and discovery _(coming soon)_
- 🚧 **Task** - Async task execution _(coming soon)_
- 🚧 **Phoenix.PubSub** - Distributed messaging _(coming soon)_
- ✅ **Zero overhead** - Direct BEAM interop with clean Elixir helpers
- ✅ **Gradual adoption** - Use alongside existing Elixir code
## Installation
```sh
gleam add glixir
```
Add the Elixir helper module to your project:
```elixir
# lib/glixir_supervisor.ex
# Copy the Glixir.Supervisor module from the docs
```
## Quick Start
### DynamicSupervisor
```gleam
import glixir
pub fn main() {
// Start a named dynamic supervisor
let assert Ok(supervisor) = glixir.start_supervisor_named("my_supervisor", [])
// Create a child specification
let worker_spec = glixir.child_spec(
id: "worker_1",
module: "MyApp.Worker",
function: "start_link",
args: [dynamic.string("config")]
)
// Start a child process
case glixir.start_child(supervisor, worker_spec) {
Ok(pid) -> io.println("Worker started!")
Error(reason) -> io.println("Failed to start worker: " <> reason)
}
// List all children
let children = glixir.which_children(supervisor)
io.debug(children)
// Get child counts
let counts = glixir.count_children(supervisor)
io.debug(counts)
}
```
### GenServer Interop
```gleam
import glixir
pub fn main() {
// Start an Elixir GenServer from Gleam
let assert Ok(server) = glixir.start_genserver("MyApp.Counter", dynamic.int(0))
// Type-safe calls with custom decoders
let assert Ok(count) = glixir.call_genserver(server, dynamic.string("get"), decode.int)
io.debug(count) // 0
// Cast messages (fire and forget)
let assert Ok(_) = glixir.cast_genserver(server, dynamic.string("increment"))
// Call again to see the change
let assert Ok(count) = glixir.call_genserver(server, dynamic.string("get"), decode.int)
io.debug(count) // 1
// Stop the GenServer
let assert Ok(_) = glixir.stop_genserver(server)
}
```
### Agent State Management
```gleam
import glixir
import gleam/dynamic/decode
pub fn main() {
// Start an agent with initial state
let assert Ok(counter) = glixir.start_agent(fn() { 42 })
// Get state with a decoder
let assert Ok(value) = glixir.get_agent(counter, fn(x) { x }, decode.int)
io.debug(value) // 42
// Update state
let assert Ok(_) = glixir.update_agent(counter, fn(n) { n + 10 })
// Get and update in one operation
let assert Ok(old_value) = glixir.get_and_update_agent(
counter,
fn(n) { #(n, n * 2) },
decode.int
)
io.debug(old_value) // 52
// Stop the agent
let assert Ok(_) = glixir.stop_agent(counter)
}
```
## Working with Elixir Modules
### Creating an Elixir Worker
```elixir
# lib/my_app/worker.ex
defmodule MyApp.Worker do
use GenServer
def start_link(config) do
GenServer.start_link(__MODULE__, config)
end
def init(config) do
{:ok, %{config: config, count: 0}}
end
def handle_call(:get_count, _from, state) do
{:reply, state.count, state}
end
def handle_cast(:increment, state) do
{:noreply, %{state | count: state.count + 1}}
end
end
```
### Using from Gleam
```gleam
import glixir
import gleam/dynamic
import gleam/dynamic/decode
pub fn worker_example() {
// Start the supervisor
let assert Ok(sup) = glixir.start_supervisor_named("worker_supervisor", [])
// Create worker specification
let worker_spec = glixir.child_spec(
id: "my_worker",
module: "MyApp.Worker",
function: "start_link",
args: [dynamic.string("some_config")]
)
// Start the worker under supervision
let assert Ok(worker_pid) = glixir.start_child(sup, worker_spec)
// Interact with the worker using GenServer calls
let assert Ok(count) = glixir.call_genserver(
worker_pid,
dynamic.atom("get_count"),
decode.int
)
io.debug(count) // 0
// Send a cast message
let assert Ok(_) = glixir.cast_genserver(worker_pid, dynamic.atom("increment"))
// Check the count again
let assert Ok(count) = glixir.call_genserver(
worker_pid,
dynamic.atom("get_count"),
decode.int
)
io.debug(count) // 1
}
```
## Advanced Patterns
### Worker Pool Pattern
```gleam
import glixir
import gleam/list
import gleam/int
pub fn start_worker_pool(pool_size: Int) {
let assert Ok(supervisor) = glixir.start_supervisor_named("worker_pool", [])
// Start multiple workers
list.range(1, pool_size)
|> list.map(fn(i) {
let worker_spec = glixir.child_spec(
id: "worker_" <> int.to_string(i),
module: "MyApp.PoolWorker",
function: "start_link",
args: [dynamic.int(i)]
)
glixir.start_child(supervisor, worker_spec)
})
|> list.all(fn(result) {
case result {
Ok(_) -> True
Error(_) -> False
}
})
}
```
### Fault-Tolerant Services
```gleam
import glixir
pub fn start_resilient_service() {
let assert Ok(supervisor) = glixir.start_supervisor_named("app_supervisor", [])
// Start critical services with permanent restart
let cache_spec = glixir.worker_spec("cache", "MyApp.Cache", [])
let database_spec = glixir.worker_spec("database", "MyApp.Database", [])
// Start optional services with temporary restart
let metrics_spec = glixir.SimpleChildSpec(
..glixir.worker_spec("metrics", "MyApp.Metrics", []),
restart: glixir.temporary
)
let assert Ok(_) = glixir.start_child(supervisor, cache_spec)
let assert Ok(_) = glixir.start_child(supervisor, database_spec)
let assert Ok(_) = glixir.start_child(supervisor, metrics_spec)
supervisor
}
```
## Configuration
### Restart Strategies
```gleam
import glixir
// Permanent: Always restart (default)
let permanent_worker = glixir.SimpleChildSpec(
..glixir.worker_spec("critical", "MyApp.Critical", []),
restart: glixir.permanent
)
// Temporary: Never restart
let temporary_worker = glixir.SimpleChildSpec(
..glixir.worker_spec("temp", "MyApp.TempWorker", []),
restart: glixir.temporary
)
// Transient: Restart only on abnormal termination
let transient_worker = glixir.SimpleChildSpec(
..glixir.worker_spec("transient", "MyApp.Transient", []),
restart: glixir.transient
)
```
### Custom Timeouts
```gleam
import glixir
let custom_spec = glixir.SimpleChildSpec(
..glixir.worker_spec("custom", "MyApp.Custom", []),
shutdown_timeout: 10000, // 10 seconds to shutdown gracefully
restart: glixir.permanent
)
```
## Type Safety Notes
While this library provides type-safe wrappers on the Gleam side, remember:
1. **Runtime Types**: Elixir/Erlang processes are dynamically typed at runtime
2. **Message Contracts**: Ensure message formats match between Gleam and Elixir
3. **Decoder Functions**: Always provide appropriate decoders for return values
4. **Error Handling**: Handle all potential decode and process errors
5. **Testing**: Test integration points thoroughly
## Error Handling
```gleam
import glixir
pub fn robust_worker_start() {
case glixir.start_supervisor_named("robust_sup", []) {
Ok(supervisor) -> {
let spec = glixir.worker_spec("worker", "MyApp.Worker", [])
case glixir.start_child(supervisor, spec) {
Ok(pid) -> {
io.println("Worker started successfully")
Ok(#(supervisor, pid))
}
Error(reason) -> {
io.println("Failed to start worker: " <> reason)
Error("worker_start_failed")
}
}
}
Error(_supervisor_error) -> {
io.println("Failed to start supervisor")
Error("supervisor_start_failed")
}
}
}
```
## About the Author
Built by **Rahmi Pruitt** - Ex-Twitch/Amazon Engineer turned indie hacker, on a mission to bring Gleam to the mainstream! 🚀
I believe Gleam's type safety + Elixir's battle-tested OTP = the future of fault-tolerant systems. This library bridges that gap, making OTP's superpowers accessible to Gleam developers.
**Connect with me:**
- 💼 [LinkedIn](https://www.linkedin.com/in/rahmi-pruitt-a1bb4a127/)
- 🐦 Follow my Gleam journey and indie hacking adventures
*"Making concurrent programming delightful, one type at a time."*
---
## Why Gleam + OTP?
**Gleam brings:**
- 🛡️ **Compile-time safety** - Catch errors before production
- 🧠 **Excellent inference** - Less typing, more productivity
- 🎯 **Simple syntax** - Easy to learn, hard to misuse
- ⚡ **Zero-cost abstractions** - Performance without complexity
**OTP brings:**
- 🏗️ **Battle-tested patterns** - 30+ years of production wisdom
- 🔄 **Fault tolerance** - "Let it crash" philosophy
- 📡 **Distribution** - Scale across machines effortlessly
- 🎭 **Actor model** - Simple concurrency primitives
**Together:** The most productive way to build reliable, concurrent systems.
## Coming Soon
- 🚧 **Registry** - Process registration and discovery
- 🚧 **Task** - Async task execution with timeouts
- 🚧 **Phoenix.PubSub** - Distributed pub/sub messaging
- 🚧 **Process Groups** - Process group management
- 🚧 **ETS** - Efficient in-memory storage
## Examples
Check out the `/examples` directory for complete examples:
- `supervisor_example.gleam` - Dynamic worker pool
- `genserver_example.gleam` - Counter GenServer integration
- `agent_example.gleam` - State management patterns
- `fault_tolerance_example.gleam` - Building resilient systems
## Contributing
Contributions welcome! Please read our [contributing guidelines](CONTRIBUTING.md).
## License
MIT License - see [LICENSE](LICENSE) for details.
---
**Ready to build fault-tolerant, concurrent systems with Gleam? Give glixir a try!** ⚡
---
## About the Author 👨💻
Built with ❤️ by **Rahmi Pruitt** - Ex-Twitch/Amazon Engineer turned indie hacker, on a mission to bring Gleam to the mainstream! 🚀
After years of building large-scale systems at Amazon and Twitch, I fell in love with Gleam's type safety and developer experience. But I missed the battle-tested OTP patterns that make Elixir/Erlang so powerful for building fault-tolerant systems.
**This library bridges that gap** - bringing OTP's superpowers to Gleam developers without sacrificing type safety or ergonomics.
**Connect with me:**
- 💼 **[LinkedIn: Rahmi Pruitt](https://www.linkedin.com/in/rahmi-pruitt-a1bb4a127/)** - Let's connect!
- 🐦 Follow my Gleam journey and indie hacking adventures
- 📧 Questions? Feedback? I'd love to hear from you!
*"Making concurrent programming delightful, one type at a time."*
---