polar

Your First Project

A single .px file is enough to try things out. Real programs live in a project: a folder with a polar.toml file and a src directory.

Create the project

polar init shop makes the folder, polar.toml, src/main.px and a .gitignore for you. To see what it writes, here is the same thing by hand.

Let's make a project called shop that adds up prices. Make this layout:

shop/
├── polar.toml
└── src/
    ├── main.px
    └── money.px

polar.toml only needs a name:

[project]
name = "shop"

Polar fills in the rest with defaults. Here they are written out, in case you want to change them:

[project]
name = "shop"
src = "src"              # where the .px files live
out = "dist"             # where polar build writes
main = "src/main.px"     # what polar run runs

Split code into modules

Each file is one module. Put the money helpers in src/money.px:

module Money

types
  Money = { cents: Int }

functions
  cents(n: Int) -> Money {
    { cents: n }
  }

  add(a: Money, b: Money) -> Money {
    { cents: a.cents + b.cents }
  }

  format(m: Money) -> String {
    let rest = m.cents % 100
    let pad = if rest < 10 { "0" } else { "" }

    "$#{m.cents / 100}.#{pad}#{rest}"
  }

exports
  cents
  add
  format

And use them from src/main.px:

module Main

uses
  Money as M
  Std.List

functions
  main() {
    let prices = [M.cents(250), M.cents(1999), M.cents(5)]
    let total = List.fold(prices, M.cents(0), M.add)

    Log.info("total: #{M.format(total)}")
  }

exports
  main

uses Money loads src/money.px. The as M part is optional: it lets us write M.add instead of Money.add. Only names listed under exports can be used from other modules.

Run it

Inside a project, commands don't need a path:

$ cd shop
$ polar run
total: $22.54

Build it

polar build writes JavaScript into dist/:

$ polar build
built shop (2 files) into dist in 43ms
dist/
├── main.js      main.js.map      main.d.ts
├── money.js     money.js.map     money.d.ts
├── start.mjs
└── _polar/

Every module becomes a .js file with a source map and a .d.ts type declaration. _polar/ holds the small runtime. dist/ is self-contained, so you can copy it anywhere and run it with Node:

$ node dist/start.mjs
total: $22.54

polar start does the same thing: it runs the existing build without compiling again.

Use it from JavaScript

The built modules are ordinary ES modules, so JavaScript can import them directly:

// use.mjs
import { cents, add, format } from "./dist/money.js";

console.log(format(add(cents(1050), cents(99))));
$ node use.mjs
$11.49

TypeScript sees the types from money.d.ts:

export type Money = { cents: number };

export declare function cents(n: number): Money;

export declare function add(a: Money, b: Money): Money;

export declare function format(m: Money): string;

Keep it building

While you work, --watch rebuilds on every save:

$ polar check --watch      # report errors only
$ polar build --watch      # rebuild dist/ too

Next, let's set up your editor.