SkillAgentSearch skills...

Glixir

Easy Gleam wrapper around elixir otp

Install / Use

/learn @rjpruitt16/Glixir
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Glixir 🌟

Package Version Hex Docs

A Safe(ish) 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, Registry, and more from Gleam with confidence.

Features

  • GenServer - Type-safe calls to Elixir GenServers
  • DynamicSupervisor - Runtime process supervision and management
  • Agent (now generic!) - Type-safe state management with async operations
  • Registry - Dynamic process registration and Subject lookup
  • 🚧 Task - Async task execution (coming soon)
  • Phoenix.PubSub - Distributed messaging with JSON-based type safety
  • ✅ libcluster - Automatic node discovery and clustering for distributed systems
  • Zero overhead - Direct BEAM interop with clean Elixir helpers
  • Gradual adoption - Use alongside existing Elixir code
  • syn - Distributed process registry and PubSub coordination

Type Safety & Phantom Types

A note from your neighborhood type enthusiast:

Some glixir APIs (notably process calls, GenServer, Registry) still require passing Dynamic values—this is the price of seamless BEAM interop and runtime dynamism. While decoders on return values help catch mismatches, full compile-time type safety isn't always possible... yet.

But here's the good news: We're actively rolling out phantom types and generics across the API, banishing Dynamic wherever possible and making misused actors a compile-time relic.


Type safety level

GenServer:     [■■■■■■■■□□] 80% - Request/reply types enforced by Gleam, decoder required, but runtime BEAM interop can still fail if types disagree.
Supervisor:    [■■■■■■■■■□] 90% - Phantom-typed with compile-time child spec validation, args/replies bounded by generics.
Registry:      [■■■■■■■■■□] 90% - Phantom-typed with compile-time key/message validation, requires key encoders.
Agent:         [■■■■■■■■■■] 100% - State and API fully generic and type safe!
PubSub:        [■■■■■■■■□□] 80% - JSON-based type safety with user-defined encoders/decoders. Phantom-typed for message_type.
libcluster:    [■■■■■■■□□□] 70% - Node discovery works, cluster membership is runtime dynamic
syn:           [■■■■■■■■□□] 80% - Distributed coordination with type-safe message patterns, runtime node discovery.
Task:          [□□□□□□□□□□] 0% - Not started.

Full green bars are the dream; until then, decoders are your seatbelt!


Why Not 100% Type-Safe Now?

Blame Erlang! (Just kidding, blame dynamic process boundaries.) You get strict safety inside Gleam, but as soon as you jump the BEAM-to-BEAM fence, the runtime is your playground. So, until Gleam's type system can tame Elixir's wild world, we work with decoders and are pushing hard to get you closer to total type bliss.


⚠️ Caveats: BEAM Interop Trade-offs

Glixir provides bounded type safety - the maximum safety possible while maintaining full OTP functionality. Some limitations are inherent to BEAM interop, not design flaws:

The Safety Spectrum

  • Pure Gleam: 100% compile-time safe, but no distributed features
  • Glixir: 70-90% compile-time safe + runtime validation, full OTP power
  • Raw FFI: ~20% safe, full OTP power, high risk

Unavoidable BEAM Realities

  • Process discovery - distributed systems have runtime process existence
  • Module loading - OTP's dynamic nature requires string module names
  • Cross-language boundaries - serialization between Gleam and Elixir

What You Get

Compile-time: Prevents category errors, type mixing, wrong message types
⚠️ Runtime: Process existence, module validity, message format compatibility

Bottom Line: Glixir is the sweet spot - maximum practical safety with essential functionality that core Gleam simply doesn't provide.

Want to help speed this up? File issues, suggest API improvements, or just cheer us on in the repo!


⚡ Atom Safety & Fail-Fast API

glixir now requires all process, registry, and module names to be existing atoms—typos or missing modules crash immediately with {bad_atom, ...}.

  • Avoids BEAM atom leaks and silent bugs.
  • If you typo or use a non-existent module, you'll see a loud error.
  • Pro-tip: Always reference the real module, or pre-load it in Elixir before calling from Gleam.

This makes glixir safer by default—don't trust user input as atom names!


libcluster - Automatic Node Discovery & Clustering

Scale your Gleam apps across multiple nodes with zero configuration! 🌐 libcluster provides automatic node discovery and clustering for distributed BEAM applications, with built-in support for Fly.io, Kubernetes, AWS, and local development.

import glixir/libcluster
import gleam/erlang/os
import logging

pub fn main() {
  // Auto-detect environment and start clustering
  let _ = case os.get_env("FLY_APP_NAME") {
    Ok(app_name) -> {
      // Running on Fly.io - automatic DNS-based discovery
      libcluster.start_clustering_fly(app_name)
    }
    Error(_) -> {
      // Local development - use EPMD for simplicity
      libcluster.start_clustering_local("my_app")
    }
  }
  
  // Check cluster status
  io.println("Node: " <> libcluster.current_node_name())
  io.println("Connected to: " <> string.inspect(libcluster.connected_node_names()))
  
  // Your distributed app starts here
  // syn will automatically sync across all discovered nodes!
  start_distributed_app()
}

// Production deployment with custom DNS
pub fn production_clustering() {
  // DNS-based discovery for production
  let assert Ok(_) = libcluster.start_clustering_dns(
    app_name: "my_app",
    query: "my_app.service.consul",  // Consul, Kubernetes DNS, etc
    polling_interval: 5000
  )
  
  // Monitor cluster membership
  case libcluster.is_clustered() {
    True -> {
      let nodes = libcluster.connected_nodes()
      logging.info("Clustered with " <> int.to_string(list.length(nodes)) <> " nodes")
    }
    False -> {
      logging.warning("Running in single-node mode")
    }
  }
}

// Combine with syn for distributed process coordination
pub fn distributed_workers() {
  // Start clustering
  let assert Ok(_) = libcluster.start_clustering_fly("worker_pool")
  
  // syn automatically discovers all nodes via libcluster!
  syn.init_scopes(["workers"])
  
  // This registration is visible across ALL nodes
  syn.register("workers", "worker_1", self())
  
  // Find workers on ANY node in the cluster
  case syn.whereis("workers", "worker_1") {
    Ok(#(pid, node)) -> {
      io.println("Found worker on node: " <> atom.to_string(node))
    }
    Error(_) -> io.println("Worker not found in cluster")
  }
}

Supported Clustering Strategies:

  • Fly.io - Automatic DNS discovery via APP_NAME.internal
  • Kubernetes - Service discovery via DNS or API
  • AWS EC2 - Tag-based instance discovery
  • Local Dev - EPMD for testing multi-node locally
  • Custom DNS - Any DNS-based service discovery Use Cases:
  • Distributed job queues across multiple machines
  • Fault-tolerant services with automatic failover
  • Load balancing across geographic regions
  • Consensus algorithms and leader election
  • Real-time data synchronization

syn - Distributed Process Coordination

Distributed service discovery and event streaming across BEAM nodes! 🌐

Use Erlang's battle-tested syn library for distributed coordination, consensus algorithms, and fault-tolerant process management.

import glixir/syn
import gleam/json

pub fn distributed_example() {
  // Initialize scopes at application startup
  syn.init_scopes(["worker_pools", "coordination"])
  
  // Register this process in a distributed pool
  let load_info = #(cpu_usage: 0.3, queue_size: 5)
  let assert Ok(_) = syn.register_worker("image_processors", "worker_1", load_info)
  
  // Find workers across the cluster
  case syn.find_worker("image_processors", "worker_1") {
    Ok(#(pid, #(cpu_usage, queue_size))) -> {
      // Send work to the least loaded worker
      process.send(pid, ProcessImage("photo.jpg"))
    }
    Error(_) -> // Worker not available, try another
  }
  
  // Join coordination group for consensus
  let assert Ok(_) = syn.join_coordination("leader_election")
  
  // Broadcast status for distributed coordination
  let status = json.object([
    #("node", json.string("worker_node_1")),
    #("load", json.float(0.3)),
    #("available", json.bool(True))
  ])
  
  let assert Ok(nodes_notified) = syn.broadcast_status("health_check", status)
  io.println("Status sent to " <> int.to_string(nodes_notified) <> " nodes")
}

// Advanced: Custom coordination patterns
pub fn consensus_example() {
  // Register with metadata for consensus algorithms
  let machine_status = #(queue_lengths: #(0, 5, 2), capacity: 100)
  let assert Ok(_) = syn.register("machines", "machine_1", machine_status)
  
  // Publish for distributed decision making
  let queue_status = json.object([
    #("machine_id", json.string("machine_1")),
    #("free_queue", json.int(0)),
    #("paid_queue", json.int(5)),
    #("capacity", json.int(100))
  ])
  
  let assert Ok(_) = syn.publish_json("coordination", "load_balancing", queue_status, fn(j) { j })
}

## Installation

```sh
gleam add glixir

Add the Elixir helper modules to your project:

# lib/glixir_supervisor.ex and lib/glixir_registry.ex
# Copy the helper modules from the docs

Quick Start

PubSub - Type-Safe Distributed Messaging

Real-time event broadcasting with JSON-based type safety! 📡

import glixir
import gleam/json
import gleam/dynamic/decode
import gleam/erlang/atom

// Define your message types
pub type UserEvent {
  PageView(user_id: String, page: String)
  Purchase(user_id: String, amount: Float
View on GitHub
GitHub Stars16
CategoryDevelopment
Updated25d ago
Forks0

Languages

Gleam

Security Score

80/100

Audited on Mar 5, 2026

No findings