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:

  1. Current package — files relative to package root
  2. Dependencies — packages declared in cell.toml
  3. 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
}