C#'s nullable reference types are opt-in and warn; in Keal they are the type system and they refuse. Beyond that: no inheritance, no properties, no LINQ — but records, pattern matching and string interpolation will all feel like home.
| Concept | C# | Keal |
|---|---|---|
| Record | record P(double X); | record P(val x: Float) |
| Class | class C { int N { get; set; } } | class C(var n: Int) |
| Nullable | string? s | val s: String? |
| Null-coalescing | s ?? "d" | s ?: "d" |
| Null-conditional | s?.Length | s?.length |
| Pattern switch | n switch { 0 => … } | when (n) { 0 -> … } |
| Type pattern | if (o is Circle c) | when (o) { is Circle(r) -> … } |
| Generic | T First<T>(List<T> xs) | func first<T>(xs: List<T>): T |
| Interface | interface IOrd | trait Ord |
| Interpolation | $"hello {name}" | "hello ${name}" |
| Async | async Task<T> / await | actors: spawn / send / run |
String and String? are different types, always, with no flag to turn it off and no annotation to forget.
A field access is a field access. If something computes, it is a method and it has parentheses.
map, filter, fold, and a lazy Sequence pipeline written in Keal itself — nothing runs until a terminal operation pulls.
keal build produces a native executable with no VM under it, and the compiler itself has zero dependencies.