polar

Modules and Packages

Every Polar file is a module. Modules are grouped into projects, which you run, and packages, which other projects depend on.

Importing with uses

The uses zone lists the modules a file needs. There are three forms:

uses
  Std.List
  Money as M
  Std.Prelude { show }
  • Std.List imports the module under its last name, so you write List.map.
  • Money as M picks a different name, so you write M.add.
  • Std.Prelude { show } also brings trait methods into scope, so you can call show(x) without a prefix.

Where modules come from

  • Paths starting with Std. come from the standard library.
  • Paths starting with a dependency's module name come from that package.
  • Everything else comes from your project's src. uses Money loads src/money.px, which declares module Money.

Exports

The exports zone lists the functions, constants, effects and hosts that other modules can use. Anything not listed stays private to the file.

Types are different: they're always exported, so they never go in the list. If you add one anyway, Polar tells you to remove it:

error[POLAR0301]: `Shape` is a type, and types are always exported
   --> tour.px:115:3
    |
115 |   Shape
    |   ^^^^^
    |
    = help: remove it from `exports`

Packages

A package is a library: it's checked and depended on, but not run by itself. Its polar.toml has a [package] section instead of [project], and claims one top-level module name:

[package]
name = "simple_framework"
module = "Framework"   # claims every `uses Framework.…`

Inside the package, file names leave out the claimed name: src/html.px is module Html, and everyone imports it as Framework.Html, including the package's own files.

Dependencies

Projects and packages list their dependencies by path:

[dependencies]
simple_framework = { path = "../simple_framework" }

Paths are relative to the polar.toml. Dependencies compile into dist/_deps/<package>/, so the build stays self-contained.

Launchers

Some programs need more than main to start. A web app built for Browser and Node needs a server that hands out the browser bundle, answers bridge calls and runs routes. A package can provide that as a launcher, a Node script:

# in the package
[launcher]
script = "launcher/serve.mjs"

A project picks a launcher from one of its direct dependencies, and can pass it options:

# in the project
[run]
launcher = "simple_framework"
options = { port = 3000 }

Now polar run builds every host and hands the result to the launcher. The launcher receives a manifest file describing the build:

{ "version": 1, "project": "web_posts", "root": "/abs/projects/web_posts",
  "out": "/tmp/polar-run-…/dist", "main": "main.js",
  "hosts": { "Browser": "/tmp/…/dist/Browser", "Node": "/tmp/…/dist/Node" },
  "options": { "port": 3000 } }

polar build bundles the launcher too, so polar start or node dist/start.mjs runs the built app without compiling again.

Note: The launcher script is copied on its own, so it can't import files that sit next to it.

Packages can also add new zones to the language. That's the last guide: Zone Plugins.