polar

A statically typed language that compiles to JavaScript.

Records, effects and hosts are checked before anything runs. What you ship is plain JavaScript with source maps and type declarations.

Functions ask only for the fields they use

Records are row-polymorphic. title_of accepts any record with a title, whether it's a Task, a Post or one written inline, and every field access is still checked.

types
  Task = { id: Int, title: String, done: Bool } derive(Eq, Json)

functions
  title_of(r: { title: String | rest }) -> String {
    r.title
  }

  complete(t: Task) -> Task {
    { ..t, done: true }
  }

Side effects are part of the type

/ {Posts} says this handler reads and writes posts, and nothing else. Functions without effects stay pure, and List.map passes its callback's effects through, so nothing slips past the checker.

effects
  Db in Node {
    find(id: Int) -> Option<Post>
    count() -> Int
  }

functions
  show(id: Int) -> Response / {Posts} {
    match Posts.find(Id(id)) {
      Some(post) -> Response.json(200, post),
      None -> Response.not_found(),
    }
  }

One program for the browser and the server

Declare the hosts your code runs on. polar build writes one bundle per host, each with only that host's bindings. A bridge lets the browser call an effect that lives in Node, with the same types on both ends.

hosts
  Browser
  Node

schema
  posts in Node
    id         Id<Post>  primary
    title      String
    author_id  Id<User>  references users

binds
  Posts in Browser from Node

Packages can add to the language

A package can define new zones with a Rust plugin. The example web framework adds schema, routes and views, and the code they generate goes through the same type checker as yours.

routes
  GET     /posts      -> index
  GET     /posts/:id  -> show
  POST    /posts      -> create

views
  card(post: Post)
    <article class="post">
      <h2>{post.title}</h2>
      <p>Post #{post.id}</p>
    </article>

JavaScript you can read

The output is ES modules with source maps and .d.ts files, so Polar fits into an existing Node or browser project. The build folder runs on its own with node dist/start.mjs.

$ polar build src --out dist
$ ls dist
main.js  main.js.map  main.d.ts  start.mjs

$ polar check src --watch
$ polar fmt src --check

Build from source

Polar needs Rust 1.85 or newer and Node.js. The docs cover the language, projects, packages and the standard library.

$ curl -fsSL https://polar-lang.vercel.app/install.sh | sh
$ polar run hello.px
module Main

functions
  main() {
    Log.info("Hello from Polar")
  }

exports
  main