## Everything is an Actor

David Khourshid - @davidkpiano

π§

βοΈβ

π

πΆβ

πΆ

πβ

βοΈ

βοΈ

π§

I would like a coffee...

What would you like?

Β ActorΒ

I would like a coffee, please.

Β ActorΒ

π­

π¬

π¬

Here you go. βοΈ

Thanks!

## What is an actor?

Actor π§

βοΈ

βοΈ

``````someActor.send({
type: 'greet',
value: 'Hei!',
from: self
});

// ...

switch (message.type) {
case 'greet':
message.from.send({
type: 'greet',
value: '...',
from: self
});
// ...
}``````

Send

## The Actor Model

A Universal Modular ACTOR Formalism for Artificial Intelligence (1973)

Carl Hewitt, Peter Bishop, Richard Steiger

Alan Kay

## Rules of actors

π€ All computation is performed within an actor

π© Actors can communicate only through messages

π₯ In response to a message, an actor can:

π¬ Send messages to other actors

π¨βπ§βπ¦ Create a finite number of child actors

π (π₯)

Behavior

State

π­ Change its state/behavior

## Rules of actors

A

B

βοΈ

βοΈ

C

βοΈ

π€ All computation is performed within an actor

π© Actors can communicate only through messages

π₯ In response to a message, an actor can:

π¬ Send messages to other actors

π¨βπ§βπ¦ Create a finite number of child actors

π­ Change its state/behavior

## Rules of actors

A

βοΈ

π€ All computation is performed within an actor

π© Actors can communicate only through messages

π₯ In response to a message, an actor can:

π¬ Send messages to other actors

π¨βπ§βπ¦ Create a finite number of child actors

π­ Change its state/behavior

A β A'

## Rules of actors

A

βοΈ

π€ All computation is performed within an actor

π© Actors can communicate only through messages

π₯ In response to a message, an actor can:

π¬ Send messages to other actors

π¨βπ§βπ¦ Create a finite number of child actors

π­ Change its state/behavior

B

βοΈ

## Rules of actors

A

βοΈ

π€ All computation is performed within an actor

π© Actors can communicate only through messages

π₯ In response to a message, an actor can:

π¬ Send messages to other actors

π¨βπ§βπ¦ Create a finite number of child actors

π­ Change its state/behavior

π

π

## Behavior

idle

making coffee

delivering coffee

βοΈ ordered

βοΈ delivered

``````{
orders: [/* ... */],
inventory: {
// ...
},
sales: 134.65
}``````

Finite State (behavior)

Extended State (data)

## Actor System

Root (Guardian) Actor

`cafe`
`cafe/manager1`
`cafe/manager1/employee1`

π«

π«

π«

π«

π«

π«

π«

βοΈ

state 1

βοΈ

state 2

βοΈ

βοΈ

processing...

state ??

βοΈ

βοΈ

βοΈ

βοΈ

βοΈ

state 1

βοΈ

βοΈ

βοΈ

βοΈ

state 1

βοΈ

state 2

βοΈ

βοΈ

βοΈ

state 1

βοΈ

state 2

βοΈ

βοΈ

state 3

βοΈ

βοΈ

state 1

βοΈ

state 2

βοΈ

βοΈ

state 3

βοΈ

state 4

βοΈβ

π»

π°

ActorRef

βοΈ

βοΈ

## Fault Tolerance

βοΈβ

π»

ActorRef

βοΈ

β

π»

βοΈ

``````const popup = window.open(/* ... */);

popup.postMessage(/* ... */);

// do something with the message
}

``````function receiveMessage(msg) {
// do something with the message

msg.source.postMessage(/* ... */);
}

``````const popup = window.open(/* ... */);

popup.postMessage(/* ... */);

// do something with the message
}

``````function receiveMessage(msg) {
// do something with the message

msg.source.postMessage(/* ... */);
}

`merge(...)`
`  scan(reducer, initial)`

initial

next

```pipe(

)```

Behavior

Actor

Parent

• Any event

SEND

• Any event

## Actors: Promise

Promise

Parent

• Resolved value
• Rejected value
• AbortController signal

SEND

or

## Actors: Observable

Observable

Parent

• Multiple values
• Completion signal
• Error signal

Nothing *

SEND

* RxJS has Subjects for this

or

`npm install xstate`
``````import { createMachine } from 'xstate';

const machine = createMachine({
initial: 'idle',
states: {
idle: {
on: { SEARCH: 'searching' }
},
searching: {
on: {
RESOLVE: 'success',
REJECT: 'failure',
SEARCH: 'searching'
}
},
success: {
on: { SEARCH: 'searching' }
},
failure: {
on: { SEARCH: 'searching' }
}
}
});``````
``````import { createMachine } from 'xstate';

const machine = createMachine({
// ...
});

const searchingState = machine
.transition('idle', 'searching');

searchingState.value;
// => 'searching'``````
``````import { createMachine, interpret } from 'xstate';

const machine = createMachine({
// ...
});

const service = interpret(machine)
.onTransition(state => console.log(state))
.start();

service.send('SEARCH');
// State {
//   value: 'searching',
//   ...
// }``````
``````import { createMachine, interpret } from 'xstate';
import { from } from 'rxjs';

const machine = createMachine(/* ... */);

const service = interpret(/* ... */).start();

const state\$ = from(service);

state\$.subscribe(/* ... */);``````
``````import { createMachine } from 'xstate';

const cafeMachine = createMachine({
// ...
states: {
// ...
makingCoffee: {
invoke: {
id: 'espresso',
src: espressoMachine,
onDone: 'deliveringCoffee'
}
},
deliveringCoffee: {
// ...
}
}
});``````

Invoke a new "actor"

with espressoMachine behavior

`"done.invoke.espresso"`
``````import { createMachine } from 'xstate';

const cafeMachine = createMachine({
// ...
states: {
// ...
makingCoffee: {
invoke: {
id: 'espresso',
src: espressoMachine,
onDone: 'deliveringCoffee'
}
},
deliveringCoffee: {
// ...
}
}
});``````

cafeMachine

espressoMachine

done.invoke.espresso

``````import { createMachine, send } from 'xstate';

const cafeMachine = createMachine({
// ...
states: {
// ...
makingCoffee: {
invoke: {
id: 'espresso',
src: espressoMachine,
onDone: 'deliveringCoffee'
},
on: {
CHANGE_ORDER: {
actions: send((_, event) => event, {
to: 'espresso'
})
}
}
},
// ...
}
});``````

cafeMachine

espressoMachine

CHANGE_ORDER

``````import { createMachine, forwardTo } from 'xstate';

const cafeMachine = createMachine({
// ...
states: {
// ...
makingCoffee: {
invoke: {
id: 'espresso',
src: espressoMachine,
onDone: 'deliveringCoffee'
},
on: {
CHANGE_ORDER: {
actions: forwardTo('espresso')
}
}
},
// ...
}
});``````

cafeMachine

espressoMachine

CHANGE_ORDER

``````import { createMachine, sendParent } from 'xstate';

const cafeMachine = createMachine({
// ...
});

const espressoMachine = createMachine({
// ...
states: {
// ...
broken: {
entry: sendParent('MACHINE_BROKEN')
},
// ...
}
});``````

cafeMachine

espressoMachine

MACHINE_BROKEN

``````import { createMachine, assign, spawn } from 'xstate';

const createOrder = (details) => {/* ... */}

const cafeMachine = createMachine({
context: {
orders: []
},
// ...
on: {
PLACE_ORDER: {
actions: assign({
orders: (context, event) => {
const order = spawn(
createOrder(event.details),
event.orderId);

return context
.orders.concat(order);
}
})
}
}
});
``````

order 1

order n

cafeMachine

. . .

``````import { createMachine, send } from 'xstate';

const cafeMachine = createMachine({
// ...
on: {
CANCEL: {
actions: send('CANCEL', {
to: (_, event) => event.orderId
})
}
}
});
``````

order 1

order n

cafeMachine

. . .

CANCEL

``````import { createMachine } from 'xstate';
import { invokeObservable } from 'xstate/invoke';

function createOrder(details) {
return /* ... */
}

const cafeMachine = createMachine({
// ...

invoke: {
src: invokeObservable(() => /* ... */)
},

// ...

on: {
ORDER: {
actions: assign({
orders: (context, event, { spawn }) => {
const order = spawn.from(
createOrder(event.details), event.id);

return context.orders
.concat(order);
}
})
}
}
});
``````

# 5.0

(tentative API)

Returns an actor creator

``````import { createBehavior, createSystem } from 'xactor';

const counter = createBehavior((state, message) => {
return {
count: state.count + 1
}
}

return state;
}, { count: 0 });

const counterSystem = createSystem(counter, 'myCounter');

counterSystem.send({
});``````
``````import { createBehavior, createSystem } from 'xactor';

const createTodo = (title) => createBehavior((state, message) => {
// ...
}, { title })

const todos = createBehavior((state, message, ctx) => {
switch (message.type) {
case 'todo.create':
const newTodo = ctx.spawn(createTodo(message.title), `todo-\${state.todos.length}`);

return {
...state,
todos: [
...state.todos,
newTodo
]
};
}
}, { todos: [] });

const todoSystem = createSystem(todos, 'myCounter');

todoSystem.send({
type: 'todo.create',
title: 'Learn the actor model!'
});``````

# Thank you ReactiveConf!

David Khourshid - @davidkpiano

#### Everything is an Actor

By David Khourshid

• 3,201