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.

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