polar

Files and Zones

A Polar file is a module, split into zones. Each zone holds one kind of declaration, so you always know where to look for something.

A file with several zones

module Todo

uses
  Std.List

types
  Task = { id: Int, title: String, done: Bool }

functions
  open_tasks(tasks: List<Task>) -> List<Task> {
    List.filter(tasks, function(t) { !t.done })
  }

exports
  open_tasks

Reading from the top:

  • uses imports modules. Std.List is the standard list module.
  • types declares the shape of a task.
  • functions defines open_tasks, which keeps only the tasks that aren't done.
  • exports makes open_tasks available to other modules.

Every zone

You only write the zones you need, but the ones you write must appear in this order, each at most once:

Zone Holds Covered in
uses Imported modules Modules and Packages
hosts Where the code runs, like Node or Browser Hosts
traits Trait declarations Traits
types Records and variants Records, Variants
constants Named values with a type Below
effects Effect declarations Effects
externs Typed JavaScript functions JavaScript Interop
binds Effect implementations Effects
functions Functions Functions
impls Trait implementations Traits
exports Public names Modules and Packages

Put them out of order and the compiler tells you the right order:

error[POLAR0208]: the `traits` zone must come before `types`
  --> traits.px:8:1
   |
 8 | traits
   | ^^^^^^
 9 |   Area<a> {
10 |     area(shape: a) -> Float
11 |   }
   |   ^
   |
   = help: zones run `uses` -> `hosts` -> `traits` -> `types` -> `constants` -> `effects` -> `externs` -> `binds` -> `functions` -> `impls` -> `exports`, each at most once

Note: Packages can add zones of their own, such as routes or views. See Zone Plugins.

Constants

The constants zone holds values that never change. Each one has a name, a type and a value:

constants
  pi: Float = 3.141592653589793
  greeting: String = "hello"

Comments

// starts a comment that runs to the end of the line. /// starts a doc comment, which describes the declaration below it:

functions
  /// Adds two numbers.
  add(a: Int, b: Int) -> Int {
    // no overflow checks here
    a + b
  }

Now that we know where things go, let's look at what goes inside a function: values and expressions.