Records that fit any shape
title_of asks for a title and nothing
more. A Task works, an inline record works, and a
record without a title is a compile error.
types
Task = { id: Int, title: String }
functions
// `| rest` lets any other fields through
title_of(r: { title: String | rest }) -> String {
r.title
}
main() {
let task: Task = { id: 1, title: "Write docs" }
Log.info(title_of(task))
Log.info(title_of({ title: "Ship it", author: "Ann" }))
}
$ polar run records.px
Write docs
Ship it
More on records
Effects are in the signature
/ {Clock} says stamp reads the clock.
double claims to be pure, so the compiler rejects
the hidden call and says how to fix it.
effects
Clock in Node {
now() -> Int
}
functions
stamp(label: String) -> String / {Clock} {
"#{label} at #{Clock.now()}"
}
double(n: Int) -> Int {
n * Clock.now()
}
$ polar check clock.px
error[POLAR0808]: `double` uses `Clock`, but its signature says it is pure
--> clock.px:17:9
|
17 | n * Clock.now()
| ^^^^^^^^^^^ this uses `Clock`
|
= note: a signature with `->` and no `/ {…}` promises no effects
= help: add `/ {Clock}` after the return type
More on effects
Browser and server in one program
Visits lives in Node and Dom in the
browser. The bridge line lets browser code call
Visits over HTTP, and the build writes one bundle
per host.
hosts
Browser
Node
effects
Visits in Node {
count() -> Int
}
binds
Visits in Node {
count() {
42
}
}
Visits in Browser from Node
functions
start() -> {} / {Dom, Visits} {
Dom.append_text("p", "#{Visits.count()} visits so far")
}
$ polar build
built visits (1 file) for Browser into dist/Browser in 118ms
built visits (1 file) for Node into dist/Node in 191ms
More on hosts
Zones that extend the language
routes isn't built into Polar. The
simple_framework package adds it with a Rust
plugin. :id fills the handler's
id parameter, and the generated router is
type-checked like your own code.
routes
GET /posts -> index
GET /posts/:id -> show
functions
index() -> Response / {Posts} {
Response.json(200, Posts.all())
}
show(id: Int) -> Response / {Posts} {
match Posts.find(Id(id)) {
Some(post) -> Response.json(200, post),
None -> Response.not_found(),
}
}
More on zones
Readable JavaScript out
Each module becomes an ES module with a source map and a
.d.ts file, so TypeScript code can import it with
full types.
types
User = { name: String, admin: Bool }
functions
greet(user: User) -> String {
if user.admin { "Admin #{user.name}" } else { user.name }
}
$ polar build greet.px --out dist
built 1 file in 5ms
$ cat dist/greet.js
import * as $rt from "./_polar/runtime.js";
export function greet(user) {
if (user.admin) {
return `Admin ${user.name}`;
} else {
return user.name;
}
}
//# sourceMappingURL=greet.js.map
$ cat dist/greet.d.ts
export type User = { name: string; admin: boolean };
export declare function greet(user: User): string;
More on the output