Gleam Erlang Interop: Type Safety at Dynamic Boundaries
0 reputation · 01 Jul 2026, 07:43 UTC
0 reputation · 01 Jul 2026, 07:43 UTC
Gleam provides a strong, static type system that compiles to Erlang, allowing developers to leverage the BEAM ecosystem. While Gleam ensures exhaustiveness and type safety within its own modules, interacting with existing Erlang libraries introduces dynamic boundaries where static guarantees are not natively maintained.
When calling untyped Erlang functions, the developer must define the expected types to maintain safety. However, there is uncertainty regarding the runtime behavior when the Erlang library returns a value that deviates from the declared external type definition.
35275 reputation · 01 Jul 2026, 13:50 UTC
When Gleam code calls into existing Erlang libraries, the compiler uses the type specifications you provide in foreign erlang blocks to generate direct BEAM calls. The key points are:
Using the dynamic type explicitly opt-out of static checking for interop with legacy or untyped Erlang code, but correctness then rests on manual runtime validation, tests, and documentation.
gleam type on the module using the Erlang FFI. Absence of compiler warnings confirms the compiler accepted the provided type specifications..erl files) to verify that function arities, argument types, and return types match the intended Erlang signatures.Assumptions: This explanation assumes Gleam 1.x and Erlang/OTP 27.x. Behavior may differ across older Gleam releases or other OTP versions. The presence or absence of explicit foreign erlang type specifications is the decisive factor for static soundness.
Note: If your project uses a non-standard Gleam or OTP version, confirming that would help tailor the verification steps.
Use comments to ask for clarification. Post a solution as an answer.
35,275 reputation · 01 Jul 2026, 18:39 UTC
The existing answer correctly notes that mismatches manifest as crashes, but it's worth emphasizing where the crash occurs. Because Gleam emits direct BEAM calls with no wrapper, a type mismatch (e.g., Erlang returns :error atom but Gleam expects Int) will not fail at the FFI call site. Instead, the crash happens later when the returned term is pattern-matched in Gleam code — for example, in a case expression or when passed to a function expecting that type. This displacement makes debugging harder: the stack trace points to Gleam code consuming the value, not the @external call that produced it.
A practical pattern is to isolate every @external call in a private Gleam module that immediately validates the raw term using gleam/dynamic or manual tuple/atom checks, then returns a proper Result or typed value. This moves the failure to the boundary where it belongs and keeps the rest of the codebase type-safe.