Hosts
A host is a place your code runs, like
Node or Browser. Polar uses each
function's effects to work out which hosts it can run on, and builds
a separate bundle for each.
Declaring hosts
A module lists the hosts it targets in the hosts zone.
Every effect then says which host it lives on:
hosts
Node
effects
Clock in Node {
now() -> Int
}
The standard library declares the two common hosts with their
effects. Std.Dom provides Browser and its
Dom effect, and Std.Process provides
Node and its Process effect.
Where code can run
You never mark a function with a host yourself. Polar works it out from its effects:
- A pure function runs on every host.
-
A function that uses
Clockruns only whereClockexists:Node. - A function that uses two effects runs only where both exist.
You can ask Polar what it decided with --emit=hosts.
Here's the program from
Effects:
$ polar build clock.px --emit=hosts
double : every host
main : {Node}
stamp : {Node}
Code that can't run anywhere
This function adds text to the page using Dom, which
only exists in the browser. It also reads a count using
Visits, which lives in Node:
start() -> {} / {Dom, Visits} {
Dom.append_text("p", "#{Visits.count()} visits so far")
}
No host has both, and Polar says so before you ship it:
error[POLAR0811]: `start` can't run anywhere
--> hosts_bad.px:23:3
|
23 | start() -> {} / {Dom, Visits} {
| ^^^^^ no host provides both `Visits` and `Dom`
24 | Dom.append_text("p", "#{Visits.count()} visits so far")
| --------------- `Dom` runs on {Browser}
| ------------ `Visits` runs on {Node}
|
= help: split the work: call a function that uses `Visits` from one host and one that uses `Dom` from the other
One way to fix this is a bridge, which makes
Visits callable from the browser over HTTP. That has
its own page: Bridges.
Building for several hosts
List the hosts in your project's polar.toml:
[project]
name = "visits"
hosts = ["Browser", "Node"]
polar build then writes one complete bundle per host.
Each one contains only that host's binds, so browser code never
ships your server's database code:
$ polar build
built visits (1 file) for Browser into dist/Browser in 118ms
built visits (1 file) for Node into dist/Node in 191ms
To build or run just one, pass --host:
$ polar build --host Node
$ polar run --host Node
Native effects and force
Some effects truly belong to one host. Mark those
native:
effects
native Canvas in Browser {
draw(shape: String) -> {}
}
You can still bind a native effect on another host, for example to
draw into an SVG buffer on the server. But that's an emulation, not
the real thing, so Polar asks you to say so with force:
binds
force Canvas in Node {
draw(shape) {
Log.info("svg buffer: #{shape}")
}
}
Without force, Polar reports that
Canvas is native to Browser. With
force where it isn't needed, you get a warning to
remove it.
You've now seen every main concept. Let's put them together in a real program: Thinking in Polar.