Actors and Modules
Cell organizes code into two types of scripts: modules (.cm) and actors (.ce).
The Actor Model
Cell is built on the actor model of computation. Each actor:
- Has its own isolated memory — actors never share state
- Runs to completion each turn — no preemption
- Performs its own garbage collection
- Communicates only through message passing
This isolation makes concurrent programming safer and more predictable.
Modules (.cm)
A module is a script that returns a value. The returned value is cached and frozen (made stone).
// math_utils.cm
var math = use('math/radians')
function distance(x1, y1, x2, y2) {
var dx = x2 - x1
var dy = y2 - y1
return math.sqrt(dx * dx + dy * dy)
}
function midpoint(x1, y1, x2, y2) {
return {
x: (x1 + x2) / 2,
y: (y1 + y2) / 2
}
}
return {
distance: distance,
midpoint: midpoint
}
Key properties:
- Must return a value — it's an error not to
- Executed once per actor — subsequent
use()calls return the cached value - Return value is stone — immutable, safe to share
- Modules can import other modules with
use()
Using Modules
var utils = use('math_utils')
var d = utils.distance(0, 0, 3, 4) // 5
Actors (.ce)
An actor is a script that does not return a value. It runs as an independent unit of execution.
// worker.ce
log.console("Worker started")
$_.on_message = function(msg) {
log.console("Received:", msg)
// Process message...
}
Key properties:
- Must not return a value — it's an error to return
- Has access to actor intrinsics (functions starting with
$) - Runs until explicitly stopped or crashes
Actor Intrinsics
Actors have access to special functions prefixed with $:
$me
Reference to the current actor.
log.console($me) // actor reference
$_.stop()
Stop the current actor.
$_.stop()
$send(actor, message, callback)
Send a message to another actor.
$send(other_actor, {type: "ping", data: 42}, function(reply) {
log.console("Got reply:", reply)
})
Messages are automatically splatted — flattened to plain data without prototypes.
$start(callback, program)
Start a new actor from a script.
$start(function(new_actor) {
log.console("Started:", new_actor)
}, "worker")
$delay(callback, seconds)
Schedule a callback after a delay.
$delay(function() {
log.console("5 seconds later")
}, 5)
$clock(callback)
Get called every frame/tick.
$clock(function(dt) {
// Called each tick with delta time
})
$receiver(callback)
Set up a message receiver.
$receiver(function(message, reply) {
// Handle incoming message
reply({status: "ok"})
})
$portal(callback, port)
Open a network port.
$portal(function(connection) {
// Handle new connection
}, 8080)
$contact(callback, record)
Connect to a remote address.
$contact(function(connection) {
// Connected
}, {host: "example.com", port: 80})
$time_limit(requestor, seconds)
Wrap a requestor with a timeout.
$time_limit(my_requestor, 10) // 10 second timeout
Module Resolution
When you call use('name'), Cell searches:
- Current package — files relative to package root
- Dependencies — packages declared in
cell.toml - Core — built-in Cell modules
// From within package 'myapp':
use('utils') // myapp/utils.cm
use('helper/math') // myapp/helper/math.cm
use('json') // core json module
use('otherlib/foo') // dependency 'otherlib', file foo.cm
Files starting with underscore (_helper.cm) are private to the package.
Example: Simple Actor System
// main.ce - Entry point
var config = use('config')
log.console("Starting application...")
$start(function(worker) {
$send(worker, {task: "process", data: [1, 2, 3]})
}, "worker")
$delay(function() {
log.console("Shutting down")
$_.stop()
}, 10)
// worker.ce - Worker actor
$receiver(function(msg, reply) {
if (msg.task == "process") {
var result = array(msg.data, x => x * 2)
reply({result: result})
}
})
// config.cm - Shared configuration
return {
debug: true,
timeout: 30
}