On this page:
1.1 Introduction
1.2 Concepts
1.3 Data Structures
1.3.1 Core Actor State and Reference
%actor
%actor-put
%actor-get
%actor-del
%actor-has?
%ref
%actor->%ref
1.3.2 Actor State Interaction
make-%actor
%actor-send!
%actor-recv!
register-%actor-method
unregister-%actor-method
evaluate-%actor-method
maybe-recv+  evaluate-%actor-method
1.3.3 Actor Queue Implementation
%queue
make-queue
%queue-send!
%queue-evt
%queue-ready?
%queue-recv!
%queue-close!
1.3.4 Actor Events
%evt
make-%actor-alarm-%evt
make-%actor-always-%evt
make-id-always-%evt
make-%actor-%queue-%evt
1.3.5 Actor Message Struct
%msg
make-%msg
%msg-rest
%msg-first
count-%msg-args
1.3.6 Actor Steps
%step?
sleep
break
debug
1.3.7 Actor Expressions
avail?
1.3.8 Actor Space and Time
%space
make-%space
prepare-actor
run-actor
1.4 Actor Basic Expander
define-actor
define/  provide-actor
actor-lambda
8.10

1 RRLL: Actor Basic 2.0

Dominik Pantůček <dominik.pantucek@trustica.cz>

Racket Rogue-Like Library: Actor Basic 2.0

1.1 Introduction

Actor Basic is a S-Expression language. The basic element of the Actor Basic language is a S-Expression list with abasic-lambda as its first element and it forms an Actor Basic program. The Racket representation of such program is then a procedure which accepts a single argument (the actor), passes it through the steps of the program which may modify it and eventually returns the final actor as updated by all the steps performed.

To create modules with Actor Basic programs, use the s-exp meta-language:

#lang s-exp rrll/abasic/standalone
 
...

This is a work-in-progress language focused on concepts found both in actor model, flow-oriented programming and strictly immutable data structures.

1.2 Concepts

This section clarifies various concepts used in the Actor Basic documentation. Such clarity is required as certain concepts are approached differently than the reader might be accustomed to.

Actor

An Actor is an encapsulation of unit of computation which can be programmed in imperative-like manner while not requiring shared mutable state. Actors can communicate only by message passing and can only update or directly query only their internal state.

Actor State

The state of each actor consists of two components. Firstly the current continuation - which can be seen as capturing the current position in time of this actor’s existence - and secondly the data structure containing such actor’s id, auxilliary data, incoming message queue and registered asynchronous message handlers.

The auxilliary data is just an immutable dictionary with symbols as keys and any values associated with those keys. These keys are refered to as actor variables.

Actor Step

A step is a transition from one actor state to the next one. At the end of each transaction the actor may suspend its execution or handle incoming messages.

The whole actor program is a sequence of steps that keep on creating new actor states as they are performed. Always the latest actor state is propagated into the actor’s containing actor space.

Semantically each step is a function with the following template:

(define ((step . args) act)
  expr ...)

If used without any arguments, no evaluation parentheses are required in the Actor Basic program:

...
step
...

However if arguments are given, they have to be passed as with ordinary procedure application:

...
(step 1 2 3)
...

In both cases the result of step evaluation is the next actor state of given actor.

Actor Expression

Any argument to actor steps or special forms is an actor expression.

Such expression can be a literal number, string or symbol.

If it is an identifier, it is assumed to be a procedure with the following template:

(define ((proc . args) act)
  expr ...)

It is similar to actor step, however it can return an arbitrary value and its result is used where the expression was used. It does not update the current actor state.

An expression can be a actor variable.

In order to evaluate some expression as a Racket expression, quasiquote must be used. Inside the quasiquote, unquote can be used to get back to Actor Basic expression evaluation.

(debug "~v ~v" $pos `(vec2-add ,$pos (vec2 1 0)))

This allows for arbitrary nesting of Actor Basic and Racket expressions.

Actor Variable

Actor variables are stored in the actor state auxilliary data dictionary. Each variable is represented by a single key which has to be a symbol?. The associated data can be anything.

In actor steps and expressions the variables can be read by using an identifier starting with $ followed by the variable name (key symbol).

If the auxilliary data contains a key 'some-var with value "Some value", the following expression returns "Some value":

$some-var

The variables can be updated via variable assignment step. The identifier for such step starts with $ followed by the variable name (the key symbol) with := appended to it. It is used as procedure setting such variable to a new value.

The following code updates the $some-var variable stored under the key 'some-var in the actor’s auxilliary data.

($some-var:= "Another one")

After performing this step, the value stored under given key will be "Another one".

Actor Message

Actors can send and receive asynchronous messages (from their perspective). Each message contains the following fields:

TODO: clarify reference (id, queue)

Actor Reference

Any actor can be referenced by its id number and command queue.

Actor Queue

A thread-safe queue that allows communication both between actors within the actor space and from/to the actor space.

Actors should wrap the queue usage and if there are no events in the queue, they should yield with the event that waits for the queue to become ready.

Actor Method

A procedure-like construct that is evaluated when given actor receives a message with given code. This evaluation behaves like level-triggered interrupt as any other code evaluation of the actor in question is suspended until the method finishes.

Actor Space

Contains references to current states of all actors.

The actor’s internal state is always updated in current-actorspace registry upon any update. This update is synchronous and it is safe to query the actorspace from other threads.

Actor Inheritance

#lang s-exp rrll/abasic/lang
 
(define-actor (adder)
  (method (get-name)
    "Adder")
  (method (add a b)
    `(+ a b)))
 
(define-actor (strange-adder (bias 0))
  (adder)
  (method (add a b)
    `(+ a b bias)))

1.3 Data Structures

This section describes all the internal data structures used by Actor Basic.

1.3.1 Core Actor State and Reference

 (require rrll/abasic/actor-struct) package: rrll-abasic

struct

(struct %actor (id data queue methods))

  id : fixnum?
  data : (and/c immutable/c (hash/c symbol? any/c))
  queue : %queue?
  methods : (and/c immutable/c (hash/c symbol? procedure?))
Data structure representing a running actor.

procedure

(%actor-put act key value)  %actor?

  act : %actor?
  key : symbol?
  value : any?
Updates the actor’s auxilliary data by setting or changing the value under given key to the value provided.

procedure

(%actor-get act key)  any/c

  act : %actor?
  key : symbol?
(%actor-get act key val)  any/c
  act : %actor?
  key : symbol?
  val : any/c
Gets the value associated with given key in the actor’s auxilliary data. The second form provides default value. If the first form is used and the key is not present, an exception is raised which effectively terminates the actor.

procedure

(%actor-del act key)  %actor?

  act : %actor?
  key : symbol?
Removes given key from actor’s auxilliary data.

procedure

(%actor-has? act key)  boolean?

  act : %actor?
  key : symbol?
Returns #t if given actor contains auxilliary data under given key.

struct

(struct %ref (id queue))

  id : fixnum?
  queue : %queue?
Represents a reference to actor.

procedure

(%actor->%ref act)  %ref?

  act : %actor?
Creates an actor reference from given actor.

1.3.2 Actor State Interaction

 (require rrll/abasic/actor) package: rrll-abasic

procedure

(make-%actor id)  %actor?

  id : fixnum?
Creates a new %actor? struct with given id.

procedure

(%actor-send! act msg)  void?

  act : (or/c %actor? %ref?)
  msg : %msg?
Sends a message to given actor.

procedure

(%actor-recv! act)  any/c

  act : %actor?
Blocking receive of message on given actor’s command queue.

procedure

(register-%actor-method act sym proc)  void?

  act : %actor?
  sym : symbol?
  proc : (->* () () #:rest list? (-> %actor? %actor?))
Registers a new actor method. The method can accept any number of both keyword and positional arguments and must return a procedure that performs the transition of the actor to the next state.

Must be used from within the method step form.

procedure

(unregister-%actor-method act sym)  %actor?

  act : %actor?
  sym : symbol?
Removes given symbol from actor’s method table.

procedure

(evaluate-%actor-method act msg)  %actor?

  act : %actor?
  msg : %msg?
Evaluates given actor method within the context of that actor and returns its new state. The method symbol is contained in message code.

procedure

(maybe-recv+evaluate-%actor-method act)  %actor?

  act : %actor
Checks whether there is an incoming message waiting and if so, receives it and evaluates appropriate method.

1.3.3 Actor Queue Implementation

 (require rrll/abasic/queue) package: rrll-abasic

This module implements the Actor Basic queue concept that can be used for sending messages to/from actors in their space and also between actors within that space.

struct

(struct %queue (queue lock sema [active? #:mutable]))

  queue : queue?
  lock : semaphore?
  sema : semaphore?
  active? : boolean?
Internal actor queue struct.

procedure

(make-queue)  %queue?

Creates new empty Actor Queue struct.

procedure

(%queue-send! q v)  void?

  q : %queue?
  v : any/c
Enqueues given value in the queue and increments its internal counter. If the queue was empty, its event becomes ready for synchronization.

procedure

(%queue-evt q)  evt?

  q : %queue?
Returns an event that is ready for synchronization when given queue is not empty.

The event returned should be used inside Actor Space environment if the queue is not ready.

procedure

(%queue-ready? q)  boolean?

  q : %queue?
Returns #t if there is at least one element in the queue.

procedure

(%queue-recv! q)  any/c

  q : %queue?
If there is nothing in the queue, does not return until something becomes available.

Removes the next element from the queue and returns it.

procedure

(%queue-close! q)  void?

  q : %queue?
Makes given queue inactive.

1.3.4 Actor Events

 (require abasic/events)

This module provides convenient evt? wrappers to be used by actor space for synchonizing actors’ computation. Wrapping events as custom struct allows for better displaying in actor monitor.

All the events return the waiting actor’s id upon successfull synchronization.

struct

(struct %evt (evt actor-id kind label))

  evt : evt?
  actor-id : fixnum?
  kind : symbol?
  label : (or/c #f string?)
The actual struct used for event handling within actor space.

procedure

(make-%actor-alarm-%evt act ms)  %evt?

  act : %actor?
  ms : positive?
Returns a synchronizable event that expires ms milliseconds from now.

procedure

(make-%actor-always-%evt act)  %evt?

  act : %actor?
Returns a synchronizable event that is always ready.

procedure

(make-id-always-%evt id)  %evt?

  id : fixnum?
Returns a synchronizable event that is always ready. Explicit actor id must be given.

procedure

(make-%actor-%queue-%evt act queue)  %evt?

  act : %actor?
  queue : %queue?
Returns a synchronizable event that is ready for synchronization when the given queue is not empty.

1.3.5 Actor Message Struct

 (require abasic/msg)

struct

(struct %msg (code sender kws kw-args args return))

  code : symbol?
  sender : (or/c %ref? #f)
  kws : (listof keyword?)
  kw-args : list?
  args : list?
  return : (or/c symbol? #f)
All messages passed between actors are represented by this struct.

No support for keyword arguments is provided.

procedure

((make-%msg code    
  [#:return return    
  #:sender sender    
  #:queue queue])    
  args ...)  %msg?
  code : symbol?
  return : (or/c symbol? #f) = #f
  sender : (or/c %actor? %ref #f) = #f
  queue : (or/c %queue? #f) = #f
  args : any/c
Creates new message suitable for %actor-send!. It captures all positional arguments as well as keyword arguments.

procedure

(%msg-rest msg)  %msg?

  msg : %msg?
Returns a message created by removing first positional argument of given message.

procedure

(%msg-first msg)  any/c

  msg : %msg?
Returns the first positional argument from given message.

procedure

(count-%msg-args msg)  fixnum?

  msg : %msg?
Returns the number of positional arguments contained in given message.

1.3.6 Actor Steps

 (require rrll/abasic/steps) package: rrll-abasic

This module covers the basic steps provided to Actor Basic programs.

value

%step? : contract? = (-> %actor? %actor?)

Not an actual contract?, but used in this documentation for reference. It is the step value after applying any arguments.

step

(sleep ms)  %step?

  ms : positive-real?
Yields the actor with alarm-evt? set to become ready for synchronization ms milliseconds from now.

step

(break)  %step?

Terminates the computation of current actor which also gets removed from the current actor space.

step

(debug fmt arg ...)  %step?

  fmt : string?
  arg : any/c
Sends debug message to current-logger and returns actor unchanged.

1.3.7 Actor Expressions

 (require rrll/abasic/exprs) package: rrll-abasic

This module contains basic procedures that return some arbirary value and not a new actor state. These include for example predicates or any other querying functions.

procedure

(avail? ref)  boolean?

  ref : %ref?
Returns true if given reference is a reference to a still existing actor.

1.3.8 Actor Space and Time

 (require rrll/abasic/space) package: rrll-abasic

This module provides the evaluation environment for interacting actors.

struct

(struct %space (last-id actors receiver printer))

  last-id : fixnum?
  actors : 
(and/c (hash/c fixnum? %actor?)
       immutable?)
  receiver : (or/c log-receiver? #f)
  printer : (or/c (-> vector? void?) #f)
A structure representing the current state of actor space with all actors structs in the current state. Technically speaking it is a snapshot of the actor spacetime at given continuation (time).

procedure

(make-%space)  %space?

Creates a new, empty actor space.

procedure

(prepare-actor space-box proc arg ...)

  
fixnum? (-> (or/c evt? #f)) evt?
  space-box : (box/c %space?)
  proc : 
(-> any/c ...
    (-> %actor? any))
  arg : any
Prepares actor for ticking. A new id is allocated in the actor space that will perform the scheduling. All arguments - including keyword arguments are passed on to proc upon first invocation.

Returns three values:

procedure

((run-actor main    
  [#:space-box space-box    
  #:monitor monitor])    
  arg ...)  void?
  main : (-> (arg any/c) ... (-> %actor? any))
  space-box : (box/c %space?) = (box (make-%space))
  monitor : (or/c (-> (box/c %space?) boolean?) #f) = #f
  arg : any/c
Creates and runs a new actor space with given main actor as initial actor. Inner evaluation creates procedure that can be evaluated with any arguments which will be in turn passed to the main actor of the space created.

It automatically spawns the NSA actor.

1.4 Actor Basic Expander

 (require rrll/abasic/expander) package: rrll-abasic

Grammar for Actor Basic programs is as follows:

 

program

 ::= 

( actor-lambda maybe-name ( argdefs ) steps )

 

maybe-name

 ::= 

 

  |  

identifier

 

argdefs

 ::= 

 

  |  

argdef argdefs

 

argdef

 ::= 

identifier

 

  |  

( identifier racket-expr )

 

  |  

kw identifier

 

  |  

kw ( identifier racket-expr )

 

kw

 ::= 

keyword?

 

identifier

 ::= 

valid Racket identifier?

 

racket-expr

 ::= 

valid Racket expression

 

steps

 ::= 

 

  |  

step steps

 

step

 ::= 

Actor Basic step

Actor Basic programs body is just a series of steps with each step conforming to the following grammar:

 

step

 ::= 

identifier

 

  |  

( spawn identifier args )

 

  |  

( loop identifier steps )

 

  |  

( identifier args )

 

  |  

( setter expr )

 

  |  

( method ( identifier argdefs ) steps )

 

  |  

( send expr identifier args )

 

  |  

( reply identifier args )

 

  |  

( respond arg )

 

  |  

( delete variable )

 

  |  

( atomic steps )

 

  |  

( when expr steps )

 

  |  

( register identifier )

 

  |  

( let ( aletpairs ) steps )

 

  |  

( case expr stepcases )

 

args

 ::= 

 

  |  

arg args

 

arg

 ::= 

expr

 

  |  

kw expr

 

expr

 ::= 

Actor Basic expression

 

setter

 ::= 

an identifier? starting with $ and ending with := like $id:=

 

aletpairs

 ::= 

 

  |  

aletpair aletpairs

 

aletpair

 ::= 

( identifier expr )

 

stepcases

 ::= 

 

  |  

stepcase stepcases

 

stepcase

 ::= 

( primitive steps )

 

  |  

( ( primitives ) steps )

 

primitives

 ::= 

 

  |  

primitive primitives

Actor Basic expressions use the following grammar:

 

expr

 ::= 

primitive

 

  |  

variable

 

  |  

( spawn identifier args )

 

  |  

identifier

 

  |  

( request expr identifier args )

 

  |  

sender

 

  |  

sender/id

 

  |  

racket-esc

 

  |  

( lookup identifier )

 

  |  

named-actor

 

primitive

 ::= 

number? literal

 

  |  

string? literal

 

  |  

boolean? literal

 

  |  

open  quote   identifier  close

 

variable

 ::= 

an identifier? starting with $ like $id

 

racket-esc

 ::= 

( quasiquote racket-esc-expr )

 

  |  

` racket-esc-expr

 

racket-esc-expr

 ::= 

( unquote expr )

 

  |  

, expr

 

  |  

racket-expr

 

  |  

( racket-esc-expr maybe-racket-esc-expr )

 

maybe-racket-esc-expr

 ::= 

 

  |  

racket-esc-expr

 

named-actor

 ::= 

identifier? starting with ! like !actor

syntax

(define-actor (name arg ...) step ...)

Wrapper around define and actor-lambda.

syntax

(define/provide-actor (name arg ...) step ...)

Like define-actor but also provides the new binding immediately.

syntax

(actor-lambda maybe-name (arg ...) step ...)

Compiles Actor Basic program. If maybe-name is provided, the expander uses that name for error reporting and monitoring.