Externs
An extern declaration tells Gale how to call or describe code outside Gale.
The declaration is a trusted contract: Gale checks its callers against the
signature, but cannot prove that the native implementation keeps that promise.
Functions
Section titled “Functions”Typed wrapper for a native text function. Gale callers use clean or trim.
mod externs.text
pub extern "String.trim" trim(value: String) -> String
pub fn clean(value: String) -> String { trim(value)}Gale emits a function in Externs.Text with a spec and a body that calls
String.trim(value). Other Gale code calls that function. The native call keeps
its full module name in generated Elixir; a Gale alias is resolved before
emission and cannot redirect it. Erlang functions use spellings such as
":erlang.send".
In test and development builds, Gale checks arguments and return values at Gale function boundaries. Production builds omit those checks. The declared types are your responsibility when the native function returns a shape Gale cannot infer.
If a native function may raise an expected exception, a raises clause turns
that exception into Error while a normal return becomes Ok:
Expected native exceptions become typed Result errors.
mod externs.numbers
pub extern exception "ArgumentError" ArgumentError { message: String}
pub extern "String.to_integer" to_integer(value: String) -> Integerraises ArgumentErrorThe Gale return type is Result<Integer, ArgumentError>. Other exceptions are
reraised; exits and throws are not caught.
Types and native structs
Section titled “Types and native structs”Use extern type for a named foreign value whose structure Gale does not
describe. It is nominal, but gives Gale no fields to read or recursively
validate. Here Range.new is the native constructor:
A native range stays opaque in Gale and is built by its native constructor.
mod externs.range
pub extern type Range
pub extern "Range.new" new(first: Integer, last: Integer) -> RangeUse extern struct when Gale should know fields of a native Elixir struct.
An extern exception is also a nominal native struct type. Gale can match
declared fields and use it in raises, but cannot construct or update it.
One Mix package: Plug
Section titled “One Mix package: Plug”The example adds {:plug, "~> 1.20"} to mix.exs. Plug.Test.conn creates an
in-memory request connection, so the tests make no HTTP calls. One Gale module
binds its functions, connection struct, exception, and module value:
Plug is a Mix dependency. Gale binds its functions, native connection struct, and already-sent exception.
mod externs.plug
pub extern struct "Plug.Conn" Conn { method: String, request_path: String, status: Nilable<Integer>, state: Atom}
pub extern exception "Plug.Conn.AlreadySentError" AlreadySentError { message: String}
pub extern mod "Plug.Conn" conn_module
pub extern "Plug.Test.conn" test_conn(method: String, path: String) -> Conn
pub extern "Plug.Conn.send_resp" send_resp( conn: Conn, status: Integer, body: String) -> Conn raises AlreadySentError
pub fn native_module() -> Term { conn_module}Conn is a Gale type, emitted as @type conn() :: %Plug.Conn{...}. Function
specs use conn() or Externs.Plug.conn() even though Plug defines
Plug.Conn.t(). Values remain native %Plug.Conn{} structs: Gale emits no
replacement defstruct and makes no copy. Typed field access, patterns,
construction, and updates use the native struct.
At checked Gale function boundaries, the runtime verifies the native struct
module and recursively checks every declared field. Undeclared native fields
are allowed. Nilable<T> means T | nil, as with the connection’s status
before a response is sent. Construction requires every declared field; native
defstruct defaults fill undeclared fields. Native @enforce_keys and
constructor rules still apply, so prefer native constructors such as
Plug.Test.conn when they establish invariants. Extern structs have no type
parameters in this version, and a module may declare several of them.
send_resp returns Result<Conn, AlreadySentError> in Gale. Calling it on an
already sent connection produces Error with the original native exception.
Native modules and behaviours
Section titled “Native modules and behaviours”extern mod "Plug.Conn" conn_module names the native module as a value. It
does not import or expose any of Plug’s functions; each called function needs
its own extern signature. You need extern mod only when passing a native
module value through Gale code. An implements clause can grant that value a
declared behaviour capability.
extern "GenServer" declares Server<...> maps Gale callback signatures to a
native BEAM behaviour. See Behaviours for the callback and
implementation rules.
For exact syntax and projection rules, see the language reference and type system.