Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dtl/6264459 to your computer and use it in GitHub Desktop.
Save dtl/6264459 to your computer and use it in GitHub Desktop.

The Origin of Species - GDG Berlin Golang

From Noise to Signal

The Problem

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.

A Possible Solution

Describing concurrent systems in abstract terms to make reasoning about certain characteristics of such systems possible.

Practical Benefits

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.

The Main Road: Message Passing

"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.

Actors - A physical approach to concurrent computing

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:

Erlang - A sequencial FPL with built in Actors based concurrency

Code

-module(hello).
-export([start/0]).

start() ->
   spawn(fun() -> loop() end).

loop() ->
   receive
      hello ->
         io:format("Hello, World!~n"),
         loop();

      goodbye ->
         ok
   end.

Compilation

$ erl
1> c(hello).
{ok,hello}

Starting the process

2> Pid = hello:start().
<0.36.0>

Sending the message hello

3> Pid ! hello.
Hello, world!
hello

Humus - A pure Actors-based FPL

Brief REPL Version

SEND (#Hello, #World) TO println
#Hello, #World

REPL Version

CREATE Hello WITH \msg.[
    CASE msg OF
    #hello : [ SEND (#Hello, #World) TO println ]
    END
]
SEND #hello TO Hello

#Hello, #World

CSP - A mathematic approach to concurrent computing

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:

occam

Classic occam-Code

#INCLUDE "hostio.inc"
#USE "hostio.lib"
PROC hello.world (CHAN OF SP fs, ts)
  SEQ
    so.write.string.nl(fs, ts, "Hello World!")
:

Compilation and Execution

#!/bin/bash
kroc --octran-opts="--tlp-fsts" -lhostio -lconvert -o helloworld helloworld.occ
./helloworld

Output

Hello World!

π-Calculus - A generalized mathematic approach to concurrent computing

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:

Golang Code

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)
}

Compilation and Execution

$ go run hello.go

Output

Hello World!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment