Both languages are small on purpose and compile to native code. Keal differs in three big ways: nullability is in the type instead of every pointer being nil-able, generics compile to one copy per instantiation, and failure is a throw you catch rather than a second return value.
| Concept | Go | Keal |
|---|---|---|
| Binding | x := 1 | val x = 1 |
| Mutable | var x = 1 | var x = 1 |
| Struct | type P struct { X int } | record P(val x: Int) |
| Method | func (p P) Len() int | func length(): Int (inside the class) |
| Interface | type Ord interface { … } | trait Ord { … } |
| Nil | var s *string = nil | val s: String? = null |
| Error | v, err := f(); if err != nil | try { f() } catch (e) { … } |
| Slice | []int{1, 2} | [1, 2] |
| Map | map[string]int{"a": 1} | {"a": 1} |
| Goroutine | go work(ch) | sys.spawn({ self, msg -> … }) |
| Switch | switch n { case 0: } | when (n) { 0 -> … } |
throw "…" and try { } catch (e) { }, on all three engines including native code — where it compiles to checked unwinding rather than to setjmp.
One C function per instantiation, no boxing, no dictionary passing. firstOr<T> used at Int and String becomes two plain functions.
There is no select and no channel type. An actor owns its state, messages are deep-copied on the way in, and the checker refuses a handler that reaches for shared mutable state.
Build a Go package as a c-archive and hand it to keal build; the example in the repository does exactly that.