Coming from C

Keal compiles to C11, so what you know about the machine still holds — layout, the ABI, the linker. What changes is that bounds are checked, integers do not wrap silently, memory is freed for you, and the type system knows which pointers can be null.

ConceptCKeal
Variableint x = 1;var x = 1
Constantconst int x = 1;val x = 1
Structstruct P { double x; };record P(val x: Float)
Pointer that may be nullchar *s = NULL;val s: String? = null
Arrayint a[] = {1, 2};val a = [1, 2]
Loopfor (int i=0;i<3;i++)for (i in 0..3)
Switchswitch (n) { case 0: … break; }when (n) { 0 -> … }
Function pointerint (*f)(int)val f: (Int) -> Int
malloc / freemalloc / freecounted automatically
Headerextern int f(int);extern func f(x: Int): Int = "f"
Inline Cnative """ … """

What will surprise you

You can read the C it emits

keal emit-c f.keal prints one self-contained translation unit, runtime included. Nothing is hidden, and keal layout prints every struct's offsets and padding.

Checked where C is silent

Integer overflow, division by zero and out-of-bounds indexing panic with a message and a line, on every engine — rather than being undefined.

Ownership at the boundary is written down

A String crossing to C says borrow or own; records cross by copy as headerless mirror structs. The checker refuses anything whose lifetime it cannot state.

Cycles are the one leak left

Counting frees everything but a cycle. Write weak on the back edge and it dies like anything else.