Modeling complex systems that execute computations simultaneously and probably use shared resources produces large amounts of execution paths and are hard to reason about on a machine level.
Describing concurrent systems in abstract terms to make reasoning about certain characteristics of such systems possible.
Creating tools on such ideas makes it possible to build complex concurrent systems while using high level abstractions that run on ordinary computing devices efficiently.
"If simlutaneously running computations use messages and only messages to share information, we can use this indirection for describing systems in terms of communication instead of conflict management on shared resources."
In 1973 Carl Hewitt, Peter Bishop and Richard Steiger published "A Universal Modular Actor Formalism for Artificial Intelligence" and laid the path for languages like Erlang or Rust. The paper describes an architecture build on a single kind of object: actors. Actors communicate via messages and it was stated that "The architecture is general with respect to control structure and does not have or need goto, interrupt, or semaphore primitives. The formalism achieves the goals that the disallowed constructs are intended to achieve by other more structured methods."
Tony Hoare published "Communicating Sequencial Processes" in 1978. The first version of CSP used synchronous message-passing between a fixed number of sequencial processes. It was not a process calculus but a programming language for parallel composition of those communication sequencial processes.
Sequenciality is a special case of concurrency in the Actors model theory. Actor systems are inherently concurrent.
Locality is another important characteristic of the Actor model. While processing a message, the actor can only use addresses of other actors that were sent with the message or created while the message is processed.
If an actor wants to receive a response from another actor, it sends it own address as part of the message and the second actor can respond on it.
Actors send and forget about the sent messages. Buffering is done in the environment and transparent to the user. Messages must not arrive in sending order. Queue actors can be used for compensation.
And last but not least the behaviour of actors are understood as mathematical functions that express what an Actor does when processing a certain kind of message. This makes it possible to model sharing in concurrent systems.
The unbound nondeterministic characteristics of the Actor model and its relation to mathematical logic is the the topic of Part II of this series of talks.
Let's say "Hello World!" in three different Actor-PLs:
-module(hello).
-export([start/0]).
start() ->
spawn(fun() -> loop() end).
loop() ->
receive
hello ->
io:format("Hello, World!~n"),
loop();
goodbye ->
ok
end.
$ erl
1> c(hello).
{ok,hello}
2> Pid = hello:start().
<0.36.0>
3> Pid ! hello.
Hello, world!
hello
SEND (#Hello, #World) TO println
#Hello, #World
CREATE Hello WITH \msg.[
CASE msg OF
#hello : [ SEND (#Hello, #World) TO println ]
END
]
SEND #hello TO Hello
#Hello, #World
The mother of occam, Plan9/libthread, Limbo and Go has still the S from sequencial in the acronym. In early versions of CSP all processes were sequencial and the number of processes was fix. In modern CSP processes can still operate sequentially but can also be the result of parallel composition of sequential processes. But they still interoperate only thru message passing.
In early CSP all processes had names and the number of processes was fix and the only way to communicated was by sending synchronous events (aka. messages) directly from process to process.
Later versions of CSP incorporated ideas from other process calculi (e.g. Robin Millners Calculus of Communicating Processes (CCP)) and the Actor model. Channels became part of CSP and indirect communication on buffered channels were introduced to solve problems with certain nondeterministic behaviours (guarded execution paths, etc.).
CSP is not well suited to express multiple clients accessing a concurrent system.
The bounded nondeterministic characteristics of CSP and its relation to other Algebra is the topic of Part II in this series of talks.
Hey CSP, let's be friendly and greet the world:
#INCLUDE "hostio.inc"
#USE "hostio.lib"
PROC hello.world (CHAN OF SP fs, ts)
SEQ
so.write.string.nl(fs, ts, "Hello World!")
:
#!/bin/bash
kroc --octran-opts="--tlp-fsts" -lhostio -lconvert -o helloworld helloworld.occ
./helloworld
Hello World!
The π-Calculus is based on CSS (Calculus of Communicating Systems) and shares the simplicity of λ-calculus. Names play a central role in π-calculus and the leap in abstraction that comes with it is based on the simple fact, that every names acts as a variable and a channel. This makes it possible to send channels over channels and Golang implements this in a static typed way. Fusion FTW!
The work of Robin Milner is the topic of Part III in this series of talks.
This is the moment when a full blown channel over channel π-calculus implementation of a "Hello World!" comes in handy:
package main
import (
"fmt"
)
func main() {
hello := make(chan chan string, 1)
go func() {
for x := range hello {
x <- "Hello World!"
}
}()
it := make(chan string)
hello <- it
fmt.Println(<-it)
}
$ go run hello.go
Hello World!