Keal borrowed Kotlin's shape on purpose: val/var, ? in the type, when, smart casts, data classes, trailing-lambda ergonomics. What it did not borrow is the JVM, the extension functions, the coroutines, and the second way to say things.
| Concept | Kotlin | Keal |
|---|---|---|
| Binding | val x = 1 | val x = 1 |
| Data class | data class P(val x: Int) | record P(val x: Int) |
| Nullable | val s: String? = null | val s: String? = null |
| Safe call | s?.length | s?.length |
| Elvis | s ?: "d" | s ?: "d" |
| when | when (n) { 0 -> … } | when (n) { 0 -> … } |
| Function | fun add(a: Int): Int = a | func add(a: Int): Int { a } |
| Unit function | fun log(s: String) { } | proc log(s: String) { } |
| Lambda | xs.map { it * 2 } | xs.map({ it * 2 }) |
| Interface | interface Ord | trait Ord |
| Coroutine | launch { … } | sys.spawn({ self, msg -> … }) |
Kotlin writes fun f(): Unit; Keal writes proc f() and refuses to let you use its non-result. Unit never appears in a program.
Operators come from eight traits (Add, Ord, …) that your type implements explicitly. There is one place to look for what + means.
spawn a handler, send messages, run. Compiled natively each actor is an OS thread; the interpreters run a deterministic schedule, and both print the same bytes.
and, or, not — plus xor, nand, nor, xnor and implies, all at one precedence, so mixed expressions need parentheses.
band, bor, bxor, bnot, shl, shr, ushr. Kotlin spells them as infix words too; Keal adds that they mix with nothing without parentheses, and that they bind tighter than == — so flag band 2 != 0 means what it looks like.