Tony Hoare’s
@rtoal
Thank you, Sir Tony, for
1978
There’s an issue with shared, mutable state.
Let’s see, what was that again...?
[cardreader?card || lineprinter!line] [west::DISSASEMBLE || X::SQUASH || east::ASSEMBLE] [ room::ROOM || fork(i:0..4)::FORK || phil(i:0..4)::PHIL ] X :: *[c:character; west?c -> east!c]
CmdList ::= (Decl ';' | Cmd ';')* Cmd Cmd ::= 'skip' | Assn | In | Out | Alt | Loop | Parallel Parallel ::= '[' Proc # '||' ']' Proc ::= Label? CmdList Label ::= id (Sub # ',')? '::' Sub ::= Primary | Range Primary ::= num | id Range ::= id ':' Primary '..' Primary Assn ::= Var ':=' Exp Exp ::= num | str | id | id? '(' (Exp # ',')? ')' Var ::= id | id? '(' (Var # ',')? ')' In ::= Name '?' Var Out ::= Name '!' Exp Name ::= Id ('(' Exp_int # ',' ')')? Loop ::= '*' Alt Alt ::= '[' GCmd # '▯' ']' GCmd ::= (Range # ',')? Guard '->' CmdList Guard ::= GList | In | GList ';' In GList ::= (Exp_bool | Decl) # ';'
17 "string" n person("alice",age) employee("bob",home("pasadena","ca","usa")) P() (x, y) cons(a,100) line[80]
n := n * 2 + 1 p := person(name, age) person(name, age) := q person(name, age) := person("alice", 29) c := P() P() := c e := player(class(3), loc(88,173)) (x,y) := (y,x)To match, must have same structure and same type. Assignment fails if no match.
Not a top-level command!
i ≤ 80 -> X!cardimage[i] (i:0..4)phil(i)?enter() -> diners := diners + 1 (x < 5; c := 10; true; y > c -> x := x + 1[ DIV :: *[x,y: integer, X?(x,y) -> quot, rem: integer; quot := 0; rem := x; *[ rem ≥ y -> rem := rem - y; quot := quot + 1 ]; X!(quot, rem) ] || X :: USER ]
[ fac(i:1..limit) :: *[n: integer, fac(i-1)?n -> [ n = 0 -> fac(i-1)!1 ▯ n > 0 -> fac(i+1)!n-1; r:integer; fac(i+1)?r; fac(i-1)!(n*r) ] ] || fac(0) :: USER ]
Q :: buffer: [0..9]object; in, out: integer; in := 0; out := 0; *[ in < out+10; producer?buffer[in mod 10] -> in := in+1 ▯ out < in; consumer?more() -> consumer!buffer[out mod 10]; out := out + 1 ]
[room::ROOM || fork(i:0..4)::FORK || phil(i:0..4)::PHIL] where ROOM = diners: integer; diners := 0; *[ (i:0..4)phil(i)?enter() -> diners := diners + 1 ▯ (i:0..4)phil(i)?exit() -> diners := diners - 1 ] and FORK = *[ phil(i)?pickup() -> phil(i)?putdown() ▯ phil(i-1 mod 5)?pickup() -> phil(i-1 mod 5)?putdown() ] and PHIL = *[ THINK; room?enter(); fork(i)!pickup(); fork(i+1 mod 5)!pickup(); EAT; fork(i)!putdown(); fork(i+1 mod 5)!putdown(); room!exit() ]
Concurrency and communication should be regarded as primitives of programming (not unlike assignment, sequencing, choice, repetition, and functional abstraction).
Warning: Hacky, Inefficient Example
Just to show a wee bit of Erlangmain(_) -> Max = 1000, Printer = spawn(printer, print_server, [self()]), lists:foreach( fun (N) -> spawn(prime_checker, is_prime, [N, Printer]) end, lists:seq(2, Max)), wait(Max-1). wait(0) -> io:format("~n"); wait(N) -> receive _ -> wait(N-1) end.
Inificiently checking primes here...
Perhaps a good example tho
-module(prime_checker). -export([is_prime/2]). is_prime(N, Observer) -> (fun Check(D) -> if D * D > N -> % No more divisors Observer ! N; N rem D == 0 -> % Composite Observer ! false; true -> % Keep looking Check(D+1) end end)(2).
A super generic integer printer
Yep, I’m a server!-module(printer). -export([print_server/1]). print_server(Observer) -> receive N when is_integer(N) -> io:format("~p ", [N]), Observer ! true; _ -> Observer ! false end, print_server(Observer).
But with channels, not named processes
Channels are synchronous (by default)
package main import "fmt" func Example() { ch := make(chan string) go func() {ch <- "Hello, world"}() fmt.Println(<-ch) // Output: Hello, world }
You can have a buffered channel, too
func generateMessages(ch chan string, n int) { for i := 0; i < n; i++ { ch <- "Hello" } close(ch) } func main() { messages := make(chan string, 5) go generateMessages(messages, 100) for message := range messages { fmt.Println(message) } }
Concurrent Sieve! (by Rob Pike, I think)
func generate(first chan<- int) { for i := 2; ; i++ { first <- i } } func filter(in <-chan int, out chan<- int, prime int) { for { candidate := <-in if candidate % prime != 0 { out <- candidate } } } func main() { ch := make(chan int) go generate(ch) for i := 0; i < 1000; i++ { prime := <-ch fmt.Println(prime) nextCh := make(chan int) go filter(ch, nextCh, prime) ch = nextCh } }
http://divan.github.io/posts/go_concurrency_visualize/
https://github.com/thomas11/csp
“Implementing these examples, and the careful reading of the paper required to do so, were a very enlightening experience. I found the iterative array of 4.2, the concurrent routines changing their behavior execution of 4.5, and the highly concurrent matrix multiplication of 6.2 to be particularly interesting.”
andand @cateches