Erlang Interop, Ports & NIFs
40.1 Calling Erlang
Erlang modules are atoms:
Erlang strings are charlists: pass ~c"..." when needed. Erlang deps go in mix.exs like any Hex package (rebar3 projects build automatically).
:math.sqrt(16) # 4.0
:crypto.hash(:sha256, "data") |> Base.encode16(case: :lower)
:timer.sleep(100); :timer.seconds(5)
:rand.uniform(100)
:queue.new() |> then(&:queue.in(1, &1))
:calendar.local_time()
:zlib.gzip("text")
:erlang.term_to_binary(%{a: 1}) |> :erlang.binary_to_term()
:binary.split("a,b", ",")
:io.format("~p~n", [[1, 2]])
:observer.start()40.2 Ports — talk to external OS processes
Ports are isolated — a crash in the external program can't crash the VM. Libraries: MuonTrap, Exile, erlexec.
port = Port.open({:spawn, "python3 -u worker.py"}, [:binary, :exit_status, {:line, 1024}])
Port.command(port, "hello\n")
receive do
{^port, {:data, {:eol, line}}} -> IO.puts(line)
{^port, {:exit_status, s}} -> IO.puts("exited #{s}")
end40.3 NIFs — Native Implemented Functions
Native code loaded into the VM: fastest, but a crash brings down the whole VM, and long-running NIFs block schedulers (use dirty schedulers).
Rustler — write safe NIFs in Rust:
Zigler — NIFs in Zig.
elixir_make — C NIFs via Makefiles.
Precompiled NIFs: rustler_precompiled.
defmodule MyApp.Native do
use Rustler, otp_app: :my_app, crate: "myapp_native"
def add(_a, _b), do: :erlang.nif_error(:nif_not_loaded)
end#[rustler::nif]
fn add(a: i64, b: i64) -> i64 { a + b }
rustler::init!("Elixir.MyApp.Native");40.4 Other interop
C nodes / Erlang distribution protocol clients (e.g., pyrlang).
Pythonx — embed Python in Elixir (via NIF).
Gleam — statically typed BEAM language; Gleam and Elixir modules can call each other.