Extern Declarations

Extern declarations describe functions and method stubs defined outside FormaLang (in the host runtime or a linked library). They have no FormaLang body.

Types are always declared as normal structs. Use extern impl to attach host-provided methods to a struct, and extern fn for standalone host-provided functions.

Extern Functions

A bodyless function provided by the host:

pub struct Canvas { width: I32, height: I32 }
pub struct Connection { id: I32 }

extern fn create_canvas(width: I32, height: I32) -> Canvas
extern fn connect(url: String) -> Connection
extern fn log(message: String)

extern "C" fn read(fd: I32) -> I32
extern "system" fn GetTickCount() -> I32

A bare extern fn defaults to the C calling convention. Specify "C" or "system" explicitly when the calling convention matters (e.g. Win32 stdcall on x86). Unknown ABI strings are rejected at parse time.

An extern function has no body: a body after an extern fn is a ParseError. A regular function needs a body: a function without one is the error RegularFnWithoutBody. The compiler checks a call to an extern function as it checks each call: the argument count, the labels and the types.

Extern Impl

Host-provided methods on a struct:

struct Canvas { width: I32, height: I32 }

extern impl Canvas {
  fn get_width(self) -> I32
  fn get_height(self) -> I32
  fn clear(self)
}

Rules:

  • Types are always normal structs: there is no extern type
  • Extern functions and extern impl methods have no body. A method with a body in an extern impl is the error ExternImplWithBody
  • A struct can have both a regular impl block and an extern impl block

Extern Impl on Primitive Types

Host-provided methods on built-in types like String, I32, F64. A primitive type also takes a regular impl and a trait impl, whose methods have bodies: see Impls on Primitive Types.

extern impl String {
  fn to_upper(self) -> String
  fn repeat(self, count: I32) -> String
}

extern impl I32 {
  fn abs(self) -> I32
}

A method that the prelude declares already (see below) cannot be declared again with the same signature. The second declaration is the error DuplicateDefinition, as for two methods of one type (see Each Name Appears Once):

extern impl String {
  fn len(self) -> I32   // error: the prelude declares `len` already
}

The compiler ships a prelude (src/prelude.fv). Each program sees it without a use. It declares:

TypeMethods
Stringlen, is_empty, slice, starts_with, contains, byte_at
[T] (Array<T>)len, is_empty
[K: V] (Dictionary<K, V>)len, is_empty
a range (Range<T>)len, is_empty
T? (Optional<T>)is_some, is_none, and from of From<T>
Seq<T>the combinators: see Control Flow

It also declares extern fn assert(condition: Boolean), which the examples in this guide use in run_checks(), the conversion traits From, Into, TryFrom and TryInto: see Conversions, the structs Decimal and Rational: see Decimal and Rational, and the struct Literal<T>: see A literal of a struct or an enum.

Backends bind these as host-provided extern functions through their existing extern-binding paths (wasm component imports, JS runtime bindings, etc.). The Seq combinators are the exception: each one takes a closure, which a host cannot call, so a backend lowers them itself as loop structure.

pub fn run_checks() {
  let s = "hello"
  assert(condition: s.len() == 5)
  assert(condition: s.slice(start: 1, end: 3) == "el")
  assert(condition: s.starts_with(prefix: "he"))
  assert(condition: s.contains(needle: "ll"))
  assert(condition: s.byte_at(i: 1) == 101)
  assert(condition: [1, 2].len() == 2)
  assert(condition: ["a": 1].is_empty() == false)
  assert(condition: (0..4).len() == 4)
}

s.slice(start: a, end: b) gives the bytes from offset a up to, but not including, offset b. A slice whose start is after its end is the empty string: "abc".slice(start: 2, end: 1) is "".

pub fn run_checks() {
  assert(condition: "abc".slice(start: 0, end: 2) == "ab")
  assert(condition: "abc".slice(start: 2, end: 1) == "")
}

s[i] for a String receiver desugars to s.byte_at(i) at IR lowering, so backends only see standard MethodCall shapes.