Skip to content
Kavachकवच

Coming From Another Language: JavaScript

● Beginner6 minBefore this: Coming From Another Language

From JavaScript: types checked before the program runs, == that compares contents, no null or undefined, and tasks instead of async.

Here is one small program twice: in JavaScript as you would write it, then in Kavach. Orders with a quantity and a price, a total that refuses an order with no quantity, and discount codes that may be missing. Both print the same three lines.

class Order {
  constructor(item, qty, cents) {
    this.item = item;
    this.qty = qty;
    this.cents = cents;
  }

  cost() {
    return this.qty * this.cents;
  }
}

class BadQty extends Error {
  constructor(item) {
    super(`no quantity for ${item}`);
    this.item = item;
  }
}

function total(orders) {
  for (const o of orders) {
    if (o.qty <= 0) {
      throw new BadQty(o.item);
    }
  }
  return orders.reduce((sum, o) => sum + o.cost(), 0);
}

const codes = new Map([["SPRING", 10], ["VIP", 25]]);
const orders = [new Order("pen", 3, 150), new Order("cup", 1, 900)];

let sum;
try {
  sum = total(orders);
} catch {
  sum = 0;
}
const off = codes.get("SPRING") ?? 0;
console.log(`total ${sum}, with ${off}% off ${Math.trunc((sum * (100 - off)) / 100)}`);
console.log(`WINTER gives ${codes.get("WINTER") ?? 0}%`);

const broken = [new Order("kite", 0, 500)];
try {
  console.log(`total ${total(broken)}`);
} catch (e) {
  if (!(e instanceof BadQty)) throw e;
  console.log(`refused: no quantity for ${e.item}`);
}
orders.js: javascript code
type Order {
    item: String
    qty: Int
    cents: Int

    fn cost(self) -> Int {
        self.qty * self.cents
    }
}

type OrderError = BadQty(item: String)

fn total(orders: [Order]) -> Int ! OrderError {
    for o in orders {
        if o.qty <= 0 {
            fail OrderError.BadQty(item: o.item)
        }
    }
    orders.sum(o => o.cost())
}

fn main() {
    const codes = ["SPRING": 10, "VIP": 25]
    const orders = [Order(item: "pen", qty: 3, cents: 150), Order(item: "cup", qty: 1, cents: 900)]

    const sum = total(orders) catch _ => 0
    const off = codes["SPRING"] ?? 0
    print("total {sum}, with {off}% off {sum * (100 - off) / 100}")
    print("WINTER gives {codes["WINTER"] ?? 0}%")

    const broken = [Order(item: "kite", qty: 0, cents: 500)]
    match total(broken) {
        ok(t) => print("total {t}"),
        err(OrderError.BadQty(item)) => print("refused: no quantity for {item}"),
    }
}

The program ran and printed:

kavach run orders.kav

total 1350, with 10% off 1215WINTER gives 0%refused: no quantity for kite
orders.kav: Kavach code, then what running it prints

What changes

In JavaScriptIn KavachLearn it
class Order with a constructortype Order { … }: Order(item: "pen", qty: 3, cents: 150) makes oneRecords
extendsno inheritance: a field, or an interfaceClasses and Inheritance
throw anything, then catchfail with an error of the type the signature names, ! OrderErrorErrors
null and undefinednone, only in a type that ends in ?Optionals
a === ba == b, which compares contentsComparison
new Map([["SPRING", 10]])["SPRING": 10]Making a map
template strings, ${x}"total {x}": every string can hold valuesInterpolation
orders.reduce((s, o) => s + o.cost(), 0)orders.sum(o => o.cost())map, filter, sum
every number is a float, and Math.truncInt for whole numbers, whose / gives a whole number; Decimal for exact amountsData Types
objects and arrays shared by referencevalues: b = a copiesValues, Not References
async, await and promisesspawn and .wait(), with no async and no awaitTasks
npm and package.jsonkavach.toml, and kavach addPackages
types only with TypeScriptevery type checked before the program runsData Types

Habits from other languages get a clear error that says what to write instead: see Habits from other languages.