First proposed by Carl Hewitt in 1973
Similar to original Object Oriented programming (OOP)
The Actor gets to decide what it wants to do with incoming messages. Example: A chicken Actor doesn’t have to agree to cut off its own head. https://gist.github.com/rbishop/9082539
var state = 0while (true) { const message = await mailbox.receive() state = state + 1}
// somewhere elsemailbox.deliver(message)
You might have a messge to stop things but we won't worry about that here block on receive it't the only input for an actor
function Mailbox () { var messages = [], awaiting = undefined function receive () { return new Promise(function (resolve) { if (next = messages.shift()) { resolve(next) } else { awaiting = resolve } }) } async function deliver (message) { messages.push(message) if (awaiting) { awaiting(messages.shift()) awaiting = undefined } } return {receive: receive, deliver: deliver}}
function init () { return 0 }function handle (message, count) { return count + 1}var state = init()while (true) { const message = await mailbox.receive() state = handle(message, state)}
function Actor (init, handle) { const mailbox = Mailbox() (async function run () { var state = init() while (true) { const message = await mailbox.receive() state = handle(message, state) } })() return {deliver: mailbox.deliver}}
Guarantees only this actor is able to receive from the mailbox
third argument could be self
Actors specify a concurrent program. To run the program requires an Actor System that handles.
const actors = []function start (init, handle) { return actors.push(Actor(init, handle)) - 1}async function deliver (address, message) { actors[address].dispatch(message)}ActorSystem = {start, deliver}
const message = mailbox.receive()// greedy processwhile (true) { // run forever}
// send a mutable messageconst message = []ActorSystem.dispatch(actor, message)// latermessage.push('surprise')
const message = mailbox.receive()// use global statewindow.message = message
// actor behaviourfunction init () { return null }function handle ({type, address}, state) { if (type == 'ping') { ActorSystem.dispatch(address, {type: 'pong'}) } else { console.log('Received Pong!') } return state}// runconst a1 = ActorSystem.start(init, handle)const a2 = ActorSystem.start(init, handle)ActorSystem.dispatch(a1, {type: 'ping', address: a2})
http://www.erlang-factory.com/upload/presentations/45/keynote_joearmstrong.pdf shared memory slide 29
Sending data ALWAYS has latency, and is unreliable.
“My first message is that concurrency is best regarded as a program structuring principle”
Tony Hoare
Packing huge big rocks into containers is very very difficult, but pouring sand into containers is really easy. If you think of processes like little grains of sand and you think of schedulers like big barrels that you have to fill up, filling your barrels up with sand, you can pack them very nicely, you just pour them in and it will work.
Joe Armstrong
navigator.hardwareConcurrency// 4new Worker('./actor-system.js')
Left as an exercise for the reader.
Many to Many relationship among Actors and Addresses.
$ dig +short google.com216.58.204.14
var state = init()while (true) { const message = await mailbox.receive() {outbound, state} = handle(message, state) outbound.forEach(doSend)}
handle can now be a totally pure function
http://www.di.unito.it/~dezani/papers/sto.pdf
The Hypertext Transfer Protocol (HTTP) is a stateless application- level request/response protocol that uses extensible semantics and self-descriptive message payloads for flexible interaction with network-based hypertext information systems.
Let's build a server
The Erlang view of the world is that everything is a process and that processes can interact only by exchanging messages.
Joe Armstrong
A server to run Raxx applications
We do not have ONE web-server handling 2 millions sessions. We have 2 million webservers handling one session each.
Managing Two Million Webservers
Joe Armstrong
defmodule MyServer do use GenServer def handle_call(:request, _from, state) do {:reply, :response, state} endend
defmodule Greetings do use Raxx.Server def handle_request( _request, _state) do %Raxx.Response{status: 200, headers: ["content-type", "text/plain"] body: "Hello, World!"} endend
defmodule Greetings do use Raxx.Server def handle_request( _request, _state) do response(:ok) |> set_header("content-type", "text/plain") |> set_body("Hello, World!") endend
defmodule Greetings do use Raxx.Server def handle_request( %{path: ["name", name]}, _state) do response(:ok) |> set_header("content-type", "text/plain") |> set_body("Hello, #{name}!") endend
defmodule Greetings do use Raxx.Server def handle_request( %{path: ["name", name]}, %{greeting: greeting}) do response(:ok) |> set_header("content-type", "text/plain") |> set_body("#{greeting}, #{name}!") endend
tail | data(1+) | head(request) -->Client ============================================ Server <-- head(response) | data(1+) | tail
defmodule Upload do use Raxx.Server def handle_head(%{path: ["upload"] body: true}, _) do {:ok, io_device} = File.open("my/path") {[], {:file, device}} end def handle_data(data, state = {:file, device}) do IO.write(device, data) {[], state} end def handle_tail(_trailers, state) do response(:see_other) |> set_header("location", "/") endend
GenBrowser treats clients as just another process in one continuous, if widely distributed, system. Every client gets an address to which messages can be dispatched; and a mailbox where messages are delivered.
Client joins, it is not started.
const client = await GenBrowser.start('http://localhost:8080')const {address, mailbox, send, communal} = clientconsole.log(address)// "g2gCZA ..."
Disconnected clients are not dead.
message = %{ "type" => "ping", "from" => GenBrowser.Address.new(self())}GenBrowser.send("g2gCZA ...", message)receive do message -> IO.inspect(message)end# => %{"type" => "pong"}
client.send("g2gCZA ...", {type: 'ping', from: client.address})const reply = await client.mailbox.receive({timeout: 5000})console.log("Pong received")
# Plug/Phoenix integrationcommunal = %{myProcess: GenBrowser.Address.new(MyNamedProcess)}plug GenBrowser.Plug, communal: communal
# Docker playgrounddocker run -it -e SECRET=s3cr3t -p 8080:8080 gen-browser
{:via, GenBrowser, "abc123"}
{:via, IOTSensor, "lightbulb"}
{:via, PersistantActor, "4ever"}
{:via, EmailService, "bob@example.com"}
Only difference is they are non deterministic I'm looking for communication protocol from early navys
See the code
github.com/crowdhailer/raxx
Interface for HTTP webservers, frameworks and clients.
github.com/crowdhailer/gen_browser
Actors for the client and server
Comments and questions
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |