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
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
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
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"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`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
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 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]
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
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]
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:
| Habit | From | In Kavach |
|---|---|---|
a === 1 | JavaScript | a == 1 |
null, nil, None | most languages | none, in an optional type |
type Dog: Animal | Java, C#, Python | a field, or an interface |
const match = 3 | any | match_value |
const 2nd_place | any | place_2nd |
prices.sum() | Python, JavaScript | prices.sum(p => p) |
s.slice(-4) | Python, JavaScript | s.slice(s.len() - 4, s.len()) |
(inout n) => … | C#, Swift | a named fn with inout |
log.info("…") | most languages | a LogEvent record |
let mut count = 0 | Rust | let count = 0 |
len(prices) | Python | prices.len() |
prices.length | JavaScript | prices.len() |
name.strip() | Python | name.trim() |
task.join() | Rust, Python | task.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)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)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 interfacefn 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`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`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)`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())`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) { … }`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)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`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()`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()`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()`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()`