Skip to content

Header imports

The first interop tier is deliberately small: import the header and use its declarations as Z values and types.

import stdio from "stdio.h";
function main(): i32 {
stdio.printf("hello from Z\n");
return 0;
}

Clang—not a handwritten wrapper—is the ABI authority. It supplies native signatures, layouts, calling conventions, namespaces, supported constants, availability, and source locations.

A default import keeps native declarations grouped:

import sqlite from "sqlite3.h";
const version = sqlite.sqlite3_libversion();

Selective imports expose only chosen symbols and support aliases:

import {
sqlite3_open,
sqlite3_close as closeDatabase
} from "sqlite3.h";

Both forms refer to the same header-derived declarations. The import shape does not introduce a wrapper function, runtime namespace object, or dispatch table.

Each module’s imported headers also remain available in generated native source when their types appear only in signatures or fields. You do not need a dummy native call or a duplicate import in the entry module to make those types compile.

Importing a header describes the API. Linking selects the native implementation:

{
"target": {
"name": "database-probe",
"entry": "main.zs",
"includeDirectories": ["vendor/include"],
"link": {
"directories": ["vendor/lib"],
"libraries": ["sqlite3"]
}
}
}

On Apple targets, use checked framework names rather than raw linker flags:

{
"target": {
"name": "window",
"entry": "main.zs",
"platform": "macos",
"minimumVersion": "14.0",
"link": {
"frameworks": ["AppKit"]
}
}
}

The minimum deployment version is applied consistently to header import, availability checking, generated source, and native compilation. Calling a newer API produces a Z-site diagnostic rather than a late generated-file error.

Imported records preserve their nominal identity and layout. Imported integer enums preserve their native type, including flag combinations:

import AppKit from "AppKit/AppKit.h";
const style = AppKit.NSWindowStyleMaskTitled
| AppKit.NSWindowStyleMaskClosable
| AppKit.NSWindowStyleMaskResizable;

Native nullability remains explicit as T | null. Narrow before use, or lift a nullable boundary into Z’s domain-oriented Option<T>:

const selected = Option.fromNullable(app.keyWindow);
match (selected) {
some(window) => window.center();
none => console.log("no key window");
}

Lifting a nullable pointer or Objective-C reference into Option does not invent ARC for the enum itself. It stores the same payload representation and applies whatever ownership the contained native identity already requires.

Properties and messages use ordinary member syntax:

import AppKit from "AppKit/AppKit.h";
const app = AppKit.NSApplication.sharedApplication;
app.setActivationPolicy(AppKit.NSApplicationActivationPolicyRegular);
const frame = AppKit.NSMakeRect(0.0, 0.0, 800.0, 600.0);
const style = AppKit.NSWindowStyleMaskTitled
| AppKit.NSWindowStyleMaskClosable
| AppKit.NSWindowStyleMaskResizable;
const window = AppKit.NSWindow.alloc().initWithContentRect(
frame,
styleMask: style,
backing: AppKit.NSBackingStoreBuffered,
defer: false
);
window.title = "Native Z";
window.center();
window.makeKeyAndOrderFront(null);
app.activate();
app.run();

ARC retains/releases, nullable properties, selector lookup, inheritance, and availability remain compiler-visible. Z syntax does not turn a message send into a JavaScript-style dynamic property access.

Class and instance properties have separate lookup scopes, even when their names match. For example, event.modifierFlags reads the original event’s flags; AppKit.NSEvent.modifierFlags reads the class property. An override in one scope does not hide an inherited property in the other. You can also assign a writable property directly through a returned native object, such as AppKit.NSApplication.sharedApplication.windowsMenu. This mutates the object, not the import namespace; readonly properties stay checked.

C++ namespace identity is retained under zcpp:

import native from "geometry.hpp";
const point = native.zcpp.Point({ x: 20, y: 22 });
const total = point.sum();

The current tier covers useful value, method, namespace, RAII, and move-only shapes. Templates, exception translation, arbitrary references, and complete standard-library-scale import remain active expansion areas.

A C header can prove a function’s ABI, but often cannot say:

  • who owns a returned pointer or record;
  • which function destroys it;
  • whether a status code means failure;
  • how callback context and release functions pair;
  • which thread may invoke a callback;
  • whether one pointer/length pair is a borrowed slice.

Z does not guess those facts. Add an adjacent .zd contract when they are required for safe, ergonomic use.

Runnable examples: AppKit window, curl, raylib, and SDL3.

Next: Z declaration contracts.