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