Skip to content
Kavachकवच

Classic Mistakes, Fixed

Also known as: 0.1 + 0.2 floating point error precision rounding nan overflow wrap javascript quirks gotchas pitfalls wat parseint big decimal bigdecimal bigfloat

● Beginner8 min

0.1 + 0.2 is 0.3, a NaN can't break a sort, "😀" is one character long: the everyday traps of other languages, fixed.

Every language has traps where a program runs, gives an answer, and the answer is wrong. Here are the best known, what other languages do, and what Kavach does. Every Kavach answer below was printed by the real compiler when this site was built.

Numbers

0.1 + 0.2

Elsewhere: C, Java, JavaScript, Python and Rust all print 0.30000000000000004.

fn main() {
    print(0.1 + 0.2)
    print(1.0 - 0.9)
    print(100 * 1.1)
    print(0.1 + 0.2 == 0.3)
}

The program ran and printed:

kavach run constants.kav

0.30.1110.0true
constants.kav: Kavach code, then what running it prints

The compiler works numbers written in the code out exactly, then rounds once. Values computed while the program runs still round at each step, as on every computer: for money, use Decimal, which is always exact and has no size limit.

Integers that wrap around

Elsewhere: C, C++, Java and Go wrap silently. The JDK's binary search broke on huge arrays because (low + high) / 2 overflowed into a negative index.

fn midpoint(low: Int32, high: Int32) -> Int32 {
    (low + high) / 2
}

fn main() {
    print(midpoint(10, 20))
    print(midpoint(1_500_000_000, 2_000_000_000))
}

The program ran, then stopped with a panic:

kavach run midpoint.kav

15panic: integer overflow: 1500000000 + 2000000000 doesn't fit in Int32
midpoint.kav: Kavach code, then what running it prints

Overflow is checked in every build, so the bug stops at its line instead of giving a wrong index. When the numbers are known while compiling, as in (1_500_000_000 + 2_000_000_000) / 2 on Int32, it doesn't even compile: "this always panics".

A NaN in a list breaks sorting

Elsewhere: C++ has undefined behaviour; Java, JavaScript and Python leave the list half sorted.

fn main() {
    const nan = 0.0 / 0.0
    print([3.0, nan, 1.0].sort())
    print([3.0, nan, 1.0].min() ?? 0.0)
}

The program ran and printed:

kavach run nan-sort.kav

[1.0, 3.0, NaN]1.0
nan-sort.kav: Kavach code, then what running it prints

A NaN ("not a number") sorts after every number, in sort, sort_by, min and max.

NaN turned into a whole number

Elsewhere: Undefined behaviour in C; Java and Rust quietly give 0.

fn main() {
    const nan = 0.0 / 0.0
    print(Int(nan))
}

The program ran, then stopped with a panic:

kavach run nan-int.kav

panic: NaN doesn't fit in Int
nan-int.kav: Kavach code, then what running it prints

"1" + 1

Elsewhere: JavaScript gives "11", and "10" * "2" gives 20.

fn main() {
    print("1" + 1)
}

The compiler refused the program:

kavach run no-coercion.kav

error: expected `String`, found `Int`
no-coercion.kav: Kavach code, then what running it prints

Types never change behind your back; convert on purpose with String(1) or Int("1").

Text read as a number

Elsewhere: JavaScript's parseInt("23sd") is 23.

fn main() {
    for text in ["42", "4 2", "abc"] {
        const n = Int(text) catch e => {
            print("{text}: {e}")
            continue
        }
        print(n)
    }
}

The program ran and printed:

kavach run parse.kav

424 2: `4 2` is not a valid Intabc: `abc` is not a valid Int
parse.kav: Kavach code, then what running it prints

Int(text) reads the whole text or fails, and you say what happens then.

Also fixed: x % 2 == 1, which misses negative odd numbers, gets a warning; == between computed floats gets a warning pointing to a.near(b); a constant too big for its type is a compile error that names a type that fits. Where C and Python disagree on -7 / 2, Kavach has both: / and % as C, div_floor and mod as Python (see Arithmetic). See Kinds of Errors.

Text

Length, capitals and sorting

Elsewhere: In JavaScript, Java and C#, "😀".length is 2. JavaScript's default sort puts [10, 9, 1, 2] in the order [1, 10, 2, 9].

fn main() {
    print("😀".len())
    print("ß".upper())
    print([10, 9, 1, 2].sort())
}

The program ran and printed:

kavach run text.kav

1SS[1, 2, 9, 10]
text.kav: Kavach code, then what running it prints

Text counts characters; "ß" in capitals is "SS", as Unicode says; numbers sort as numbers.

Values

A default argument shared between calls

Elsewhere: In Python, def add(x, items=[]) keeps one list for every call, so the second call returns [1, 2].

fn add(x: Int, items: [Int] = []) -> [Int] {
    let list = items
    list.push(x)
    list
}

fn main() {
    print(add(1))
    print(add(2))
}

The program ran and printed:

kavach run defaults.kav

[1][2]
defaults.kav: Kavach code, then what running it prints

A closure that sees the loop's last value

Elsewhere: JavaScript with var, and Go before 1.22, print 3 3 3.

fn main() {
    let shows: [fn() -> Int] = []
    for i in 0..3 {
        shows.push(() => i)
    }
    for show in shows {
        print(show())
    }
}

The program ran and printed:

kavach run closures.kav

012
closures.kav: Kavach code, then what running it prints

A lambda keeps a copy of each value it uses, taken when it is made.

Two names for one list

Elsewhere: In Python, Java and JavaScript, b = a shares the list, so changing b changes a.

fn main() {
    let a = [1, 2, 3]
    let b = a
    b.push(4)
    print(a)
    print(b)
}

The program ran and printed:

kavach run values.kav

[1, 2, 3][1, 2, 3, 4]
values.kav: Kavach code, then what running it prints

Habits from other languages

Code written from habit in another language gets one clear error that says what to write in Kavach, instead of a puzzle. Each of these is refused before the program runs:

HabitFromIn Kavach
a === 1JavaScripta == 1
null, nil, Nonemost languagesnone, in an optional type
type Dog: AnimalJava, C#, Pythona field, or an interface
const match = 3anymatch_value
const 2nd_placeanyplace_2nd
prices.sum()Python, JavaScriptprices.sum(p => p)
s.slice(-4)Python, JavaScripts.slice(s.len() - 4, s.len())
(inout n) => …C#, Swifta named fn with inout
log.info("…")most languagesa LogEvent record
let mut count = 0Rustlet count = 0
len(prices)Pythonprices.len()
prices.lengthJavaScriptprices.len()
name.strip()Pythonname.trim()
task.join()Rust, Pythontask.wait()
fn main() {
    const a = 1
    if a === 1 {
        print("one")
    }
}

The compiler refused the program:

kavach run triple-eq.kav

error: Kavach has no `===`: write `==`, which compares values by their contents (spec 1.8)
triple-eq.kav: Kavach code, then what running it prints
fn main() {
    let nickname: String? = null
    print(nickname ?? "no nickname")
}

The compiler refused the program:

kavach run null.kav

error: Kavach has no `null`: a value that may be missing has an optional type (`String?`), and the missing value is `none` (spec 2.7)
null.kav: Kavach code, then what running it prints
type Animal {
    name: String
}

type Dog: Animal {
    breed: String
}

The compiler refused the program:

kavach run inherit.kav

error: `Dog` can't extend `Animal`: `Animal` is a type, not an interface, and Kavach has no inheritance (decision 0029). Give `Dog` a field `animal: Animal`, or make `Animal` an interface
inherit.kav: Kavach code, then what running it prints
fn main() {
    const match = 3
    print(match)
}

The compiler refused the program:

kavach run keyword.kav

error: `match` is a keyword, so it can't be a name (spec 1.5): choose another, such as `match_value`
keyword.kav: Kavach code, then what running it prints
fn main() {
    const 2nd_place = "Ravi"
    print(2nd_place)
}

The compiler refused the program:

kavach run digit.kav

error: a name can't start with a digit (spec 1.5): start it with a letter, as `place_2nd`
digit.kav: Kavach code, then what running it prints
fn main() {
    const prices = [120, 80, 45]
    print(prices.sum())
}

The compiler refused the program:

kavach run sum.kav

error: `sum` takes a function that gives each item's number: `prices.sum(p => p)`, or `orders.sum(o => o.total)`
sum.kav: Kavach code, then what running it prints
fn main() {
    const card = "4242424242421881"
    print(card.slice(-4))
}

The compiler refused the program:

kavach run slice.kav

error: `slice` takes where to start and where to end: `s.slice(0, 4)`. Positions start at 0 and aren't negative: the last 4 are `s.slice(s.len() - 4, s.len())`
slice.kav: Kavach code, then what running it prints
fn main() {
    let count = 0
    const bump = (inout n: Int) => {
        n += 1
    }
}

The compiler refused the program:

kavach run lambda-inout.kav

error: a lambda can't have `inout` parameters (spec 3.9): give the function a name, as `fn bump(inout n: Int) { … }`
lambda-inout.kav: Kavach code, then what running it prints
use std.log

fn main() {
    log.info("order placed")
}

The compiler refused the program:

kavach run log-text.kav

error: `String` does not implement `LogEvent`: log events are records, so a log line can be searched by its fields. Declare one, as `type OrderPlaced: LogEvent { order_id: Int }`, and log that: `log.info(OrderPlaced(order_id: 7))` (spec 14.12)
log-text.kav: Kavach code, then what running it prints
fn main() {
    let mut count = 0
    count += 1
    print(count)
}

The compiler refused the program:

kavach run let-mut.kav

error: Kavach has no `let mut`: a `let` can already change, so write `let count`
let-mut.kav: Kavach code, then what running it prints
fn main() {
    const prices = [12, 30, 7]
    print(len(prices))
}

The compiler refused the program:

kavach run len.kav

error: Kavach has no `len` function: it is a method, so write `prices.len()`
len.kav: Kavach code, then what running it prints
fn main() {
    const prices = [12, 30, 7]
    print(prices.length)
}

The compiler refused the program:

kavach run length.kav

error: `[Int]` has no field `length`: Kavach's is the method `len()`
length.kav: Kavach code, then what running it prints
fn main() {
    const name = "  Asha  "
    print(name.strip())
}

The compiler refused the program:

kavach run strip.kav

error: `String` has no method `strip`: Kavach's is `trim()`
strip.kav: Kavach code, then what running it prints
fn add(a: Int, b: Int) -> Int {
    a + b
}

fn main() ! Error {
    const task = spawn add(2, 3)
    print(try task.join())
}

The compiler refused the program:

kavach run task-join.kav

error: `Task<Int>` has no method `join`: Kavach's is `wait()`
task-join.kav: Kavach code, then what running it prints