Actor

The Actor module provides lightweight concurrent actors for message-based programming. Actors are independent entities with their own state that communicate via asynchronous message passing, inspired by Erlang and Pony.

Concurrency Capability

Actor creation and actor registry operations require ActorAuth, ConcurrencyAuth, or RootAuth in scope.

Actor Model

Each actor has a mailbox for incoming messages, internal state, and a handler function. Messages are processed cooperatively via Actor.tick or automatically in threaded mode.

Creating Actors

Actor.new
(fn(msg, state) -> state) -> state -> Int
Creates a new actor with the given handler function and initial state. Returns an actor ID (positive on success, -1 on failure).
# Define a counter handler
handler = fn(msg, state) =>
  match msg
    | 1 -> state + 1
    | -1 -> state - 1
    | _ -> state

# Create the actor with initial state 0
counter = Actor.new handler 0
println "Created actor: ${counter}"

Message Passing

Actor.send
Int -> a -> Bool
Sends a message to the actor's mailbox. Returns true if sent successfully.
# Send messages using Actor.send
Actor.send counter 1   # increment
Actor.send counter 1   # increment
Actor.send counter -1  # decrement

# Or use the <- operator
counter <- 1
counter <- :reset
Actor.tick
Int -> Int
Processes all pending messages in the actor's mailbox. Returns the number of messages processed.
# Process all messages
processed = Actor.tick counter
println "Processed ${processed} messages"
Actor.tick-one
Int -> Bool
Processes one pending message from the mailbox. Returns true if a message was processed, false if mailbox was empty.
Actor.receive
Int -> Option a
Receives a result from the actor's result queue. Returns Some value if available, None if empty.
Actor.receive-all
Int -> List a
Receives all pending results from the actor. Returns an empty list if no results are available.

Lifecycle

Actor.is-alive?
Int -> Bool
Returns true if the actor is still running.
Actor.stop
Int -> Unit
Requests the actor to stop gracefully. The actor finishes processing current messages before stopping.
# Graceful shutdown
Actor.stop counter
Actor.terminate
Int -> Unit
Forcefully terminates the actor immediately. Use Actor.stop for graceful shutdown.
Actor.wait
Int -> Unit
Blocks until the actor finishes (for threaded actors).

State Access

Actor.state
Int -> a
Returns the actor's current state. Thread-safe read of actor state.
current = Actor.state counter
println "Counter value: ${current}"
Actor.current
() -> Int
Returns the current actor's ID. Returns 0 if not running inside an actor context.
Actor.mailbox-length
Int -> Int
Returns the number of messages waiting in the actor's mailbox.

Actor Registry

Actors can be registered with names for easy lookup across the application.

Actor.register
String -> Int -> Bool
Registers an actor with a name for lookup. Returns true on success, false if name is already taken.
Actor.register "my-counter" counter
Actor.lookup
String -> Option Int
Looks up an actor by its registered name. Returns Some actor-id if found, None otherwise.
match Actor.lookup "my-counter"
  | Some id -> id <- 1
  | None -> println "Actor not found"
Actor.unregister
String -> Bool
Removes an actor's name registration. Returns true if the name was registered, false otherwise.

Error Types

Actor operations that may fail use the ActorError type.

type ActorError =
  | SpawnError { message: String }
  | SendError { message: String }
  | ReceiveError { message: String }
  | ActorNotFound
  | ActorStopped
  | RegistryError { message: String }
  | ActorError { message: String }

Complete Example

# A simple counter actor
handler = fn(msg, state) =>
  match msg
    | :inc -> state + 1
    | :dec -> state - 1
    | :reset -> 0
    | _ -> state

# Create and register the actor
counter = Actor.new handler 0
Actor.register "counter" counter

# Send messages using <- operator
counter <- :inc
counter <- :inc
counter <- :dec

# Process all messages
Actor.tick counter

# Check the result
println "Final count: ${Actor.state counter}"  # => 1

# Cleanup
Actor.stop counter