Hello, Gophers!

Gophercon Opening Keynote

24 April 2014

Rob Pike

Google, Inc.

Video

A video of this talk was recorded at GopherCon in Denver.

Hello, gophers!

Hello, gophers!

package main

import "fmt"

func main() {
    fmt.Printf("Hello, gophers!\n")
}

History

This is a historic occasion.

Go has achieved a level of success worthy of a conference.

Success

Many factors contribute to that success.

Case study

A look back, focusing on code.

Two programs

A close look at two programs.

First is the first Go program you ever saw. Historic for you.
Second is the first Go program we ever saw. Historic for all gophers.

First up: "hello, world".

hello.b

main( ) {
    extrn a, b, c;
    putchar(a); putchar(b); putchar(c); putchar('!*n');
}
a 'hell';
b 'o, w';
c 'orld';

First appeared in a 1972 B tutorial by Brian W. Kernighan.
(Not, as sometimes claimed, a few years earlier in BCPL.)

hello.c

main()
{
    printf("hello, world");
}

First appeared in
Programming in C: A Tutorial, by Brian W. Kernighan, 1974.
Came as a document with Unix v5.

hello.c

main()
{
    printf("hello, world\n");
}

First appeared in
The C Programming Language, by Brian W. Kernighan and Dennis M. Ritchie, 1978.

hello.c, Draft ANSI C

#include <stdio.h>

main()
{
    printf("hello, world\n");
}

Appeared in
The C Programming Language, Second Edition, (Based on Draft-Proposed ANSI C)
by Brian W. Kernighan and Dennis M. Ritchie, 1988.

hello.c, ANSI C89

#include <stdio.h>

main(void)
{
    printf("hello, world\n");
}

Appeared in
The C Programming Language, Second Edition, round two,
by Brian W. Kernighan and Dennis M. Ritchie, 1988.

"You've gotta put a void THERE?" -Ken Thompson

A generation or two later...

(Skipping all the intermediate languages.)

Go discussions start in late 2007.

Specification first drafted in March 2008.
For experimentation and prototyping, compiler work already underway.
Initially generated C output.
Once the specification arose, compiler rewritten to generate native code.

hello.go, June 6, 2008

package main

func main() int {
    print "hello, world\n";
    return 0;
}

First checked-in test.
The print builtin is all we have, and main returns an int.
Note: no parentheses on print.

hello.go, June 27, 2008

package main

func main() {
    print "hello, world\n";
}

When main returns, the program calls exit(0).

hello.go, August 11, 2008

package main

func main() {
    print("hello, world\n");
}

Parentheses now required: print now a function not a primitive.

hello.go, October 24, 2008

package main

import "fmt"

func main() {
    fmt.printf("hello, world\n");
}

The "printf as we know and love it" goes in.
(The test still uses print not printf; we've switched examples here.)

hello.go, January 15, 2009

package main

import "fmt"

func main() {
    fmt.Printf("hello, world\n");
}

Upper case for export. "Casification."

hello.go, Dec 11, 2009

package main

import "fmt"

func main() {
    fmt.Printf("hello, world\n")
}

No more semicolons.
A major change that occurs after the open source release (Nov 10, 2009).

The current version.

It took us a while to get here (32 years!).

A lot of history.

Not just C

We "started with C" but Go is profoundly different.
Some of the languages that influenced and informed the design of Go:

C: statement and expression syntax
Pascal: declaration syntax
Modula 2, Oberon 2: packages
CSP, Occam, Newsqueak, Limbo, Alef: concurrency
BCPL: the semicolon rule
Smalltalk: methods
Newsqueak: <-, :=
APL: iota

And others. Also some was invented whole: defer, constants, for instance.

Plus lessons good and bad from all those plus:
C++, C#, Java, JavaScript, LISP, Python, Scala, ...

hello.go, Go version 1

Which brings us to today.

package main

import "fmt"

func main() {
    fmt.Println("Hello, Gophers (some of whom know 日本語)!")
}

Let's dig deeper, break this down.

Hello, world in 16 tokens

package
main
import
"fmt"
func
main
(
)
{
fmt
.
Println
(
"Hello, Gophers (some of whom know 日本語)!"
)
}

package

Major topic in early design discussions: Key to scalability.

What is a package? Ideas from Modula-2 etc.
Why are there packages?
Hold all the information you need to build.
No circular dependencies (imports).
No subpackages.
Separation of package name and package path.
Visibility is package-level, not type-level.
Within a package, you have the whole language, outside only what you permit.

main

One place where C legacy shows through.
Was originally Main for some forgotten reason.
Main package, main function.
Special because the root of the initialization tree.

import

Mechanism for loading a package.
Implemented by the compiler (as opposed to a text processor).
Worked hard to make it efficient and linear.
Imports a package, not a set of identifiers.

As for export: It used to be a keyword.

"fmt"

Package path is just a string, not a list of identifiers.
Allows the language to avoid defining what it means—adaptability.
From the beginning wanted a URL as an option.
Allows for future growth.

func

A keyword introduces functions (and types, variables, constants) for easy parsing.
Easy parsing is important with function literals (closures).

By the way, keyword was originally function.

Aside: Mail thread from February 6, 2008

From: Ken Thompson <ken@google.com>
To: gri, r

larry and sergey came by tonight. we
talked about go for more than an hour.
they both said they liked it very much.

p.s. one of larrys comments was "why isnt function spelled func?"

---

From: Rob Pike <r@google.com>
To: ken, gri

fine with me. seems compatible with 'var'.

anyway we can always say, "larry said to call it 'func'"

main

Where program starts... except it isn't.
Separation of initialization from normal execution, long planned.
Where does initialization happen?
Feeds back to package design.

()

Look Ma, no void.
No return value for main: handled by runtime.
No function args (command line is in os package).
No return value.

Return values and syntax.

{

Braces not spaces.
And not square brackets.
Why is the newline after the brace?

fmt

All imported identifiers are qualified by their import.
Every identifier is either local to package or func, or qualified by type or import.
Profound effect on readability.

Why fmt not format?

.

How many uses are there in Go for a period token? (Lots.)
The meaning of a.B requires using the type system.
But it is clear to humans and very easy to read.

Autopromotion of pointers (no -> operator).

Println

Println not println: capitals for export.
Always knew it would be reflection-driven. (Safety, formatless printing.)
Variadic functions.
Argument type was (...); became (...interface{}) on Feb 1, 2010.

(

Traditional function syntax.

"Hello, Gophers (some of whom know 日本語)!"

UTF-8 input source, so strings as literals are UTF-8 automatically.
But what is a string?
One of the first things written in the specification, hardly changed today.

)

No semicolon.
Semicolons went away shortly after release.
Much futzing around to try to cull them in early days.
Eventually accepted the BCPL approach.

}

Done.

Aside: Not discussed

Plus tools, ecosystem, community, ...:
Language is central but only part of the story.

Success

Factors:

Finally: Commitment.

Go 1.0 locked down the language and libraries.

Another round

Now watch similar evolution of a second program.

Problem: Prime sieve

Problem specification from
Communicating Sequential Processes, by C. A. R. Hoare, 1978

"Problem: To print in ascending order all primes less than
10000. Use an array of processes, SIEVE, in which each
process inputs a prime from its predecessor and prints it.
The process then inputs an ascending stream of numbers
from its predecessor and passes them on to its successor,
suppressing any that are multiples of the original prime. "

Solution

Defined in the 1978 CSP paper.
(Note: not the sieve of Eratosthenes.)

"This beautiful solution was contributed by David Gries."

CSP

In Hoare's 1978 CSP paper

[SIEVE(i:1..100):: 
    p,mp:integer; 
    SIEVE(i - 1)?p; 
    print!p; 
    mp := p; comment mp is a multiple of p; 
*[m:integer; SIEVE(i - 1)?m →
    *[m > mp → mp := mp + p]; 
    [m = mp → skip 
    ||m < mp → SIEVE(i + 1)!m 
]   ] 
||SIEVE(0)::print!2; n:integer; n := 3; 
    *[n < 10000 → SIEVE(1)!n; n := n + 2] 
||SIEVE(101)::*[n:integer;SIEVE(100)?n → print!n] 
||print::*[(i:0..101) n:integer; SIEVE(i)?n → ...] 
] 

No channels, just processes so number of primes is fixed by program.

Newsqueak

circa 1988.
Language by Rob Pike, program by Tom Cargill via Doug McIlroy.

Uses channels, so length of run is programmable.
(Where did the idea of channels come from?)

counter:=prog(end: int, c: chan of int)
{
    i:int;
    for(i=2; i<end; i++)
        c<-=i;
};

filter:=prog(prime: int, listen: chan of int, send: chan of int)
{
    i:int;
    for(;;)
        if((i=<-listen)%prime)
            send<-=i;
};

Newsqueak (cont'd)

sieve:=prog(c: chan of int)
{
    for(;;){
        prime:=<-c;
        print(prime, " ");
        newc:=mk(chan of int);
        begin filter(prime, c, newc);
        c=newc;
    }
};

count:=mk(chan of int);

begin counter(10000, count);
begin sieve(count);
"";

sieve.go, March 5, 2008

First version in a Go specification, probably the second non-trivial program written.
> to send, < to receive. Channels are pointers. Main is capitalized.

package Main

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func Generate(ch *chan> int) {
    for i := 2; ; i++ {
        >ch = i;    // Send 'i' to channel 'ch'.
    }
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in *chan< int, out *chan> int, prime int) {
    for ; ; {
        i := <in;    // Receive value of new variable 'i' from 'in'.
        if i % prime != 0 {
            >out = i;    // Send 'i' to channel 'out'.
        }
    }
}

sieve.go, March 5, 2008 (cont'd)

// The prime sieve: Daisy-chain Filter processes together.
func Sieve() {
    ch := new(chan int);  // Create a new channel.
    go Generate(ch);      // Start Generate() as a subprocess.
    for ; ; {
        prime := <ch;
        printf("%d\n", prime);
        ch1 := new(chan int);
        go Filter(ch, ch1, prime);
        ch = ch1;
    }
}

func Main() {
    Sieve();
}

sieve.go, July 22, 2008

-< to send, <- to receive. Channels still pointers. Now main not capitalized.

package main

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func Generate(ch *chan-< int) {
    for i := 2; ; i++ {
        ch -< i    // Send 'i' to channel 'ch'.
    }
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in *chan<- int, out *chan-< int, prime int) {
    for {
        i := <-in;    // Receive value of new variable 'i' from 'in'.
        if i % prime != 0 {
            out -< i    // Send 'i' to channel 'out'.
        }
    }
}

sieve.go, July 22, 2008 (cont'd)

// The prime sieve: Daisy-chain Filter processes together.
func Sieve() {
    ch := new(chan int);  // Create a new channel.
    go Generate(ch);      // Start Generate() as a subprocess.
    for {
        prime := <-ch;
        printf("%d\n",    prime);
        ch1 := new(chan int);
        go Filter(ch, ch1, prime);
        ch = ch1
    }
}

func main() {
    Sieve()
}

sieve.go, September 17, 2008

Communication operators now prefix and postfix <-. Channels still pointers.

package main

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func Generate(ch *chan <- int) {
    for i := 2; ; i++ {
        ch <- i  // Send 'i' to channel 'ch'.
    }
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in *chan <- int, out *<-chan int, prime int) {
    for {
        i := <-in;  // Receive value of new variable 'i' from 'in'.
        if i % prime != 0 {
            out <- i  // Send 'i' to channel 'out'.
        }
    }
}

sieve.go, September 17, 2008 (cont'd)

// The prime sieve: Daisy-chain Filter processes together.
func Sieve() {
    ch := new(chan int);  // Create a new channel.
    go Generate(ch);      // Start Generate() as a subprocess.
    for {
        prime := <-ch;
        print(prime, "\n");
        ch1 := new(chan int);
        go Filter(ch, ch1, prime);
        ch = ch1
    }
}

func main() {
    Sieve()
}

sieve.go, January 6, 2009

The make builtin arrives. No pointers. Code wrong! (One * left, bad argument types.)

package main

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func Generate(ch chan <- int) {
    for i := 2; ; i++ {
        ch <- i  // Send 'i' to channel 'ch'.
    }
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in chan <- int, out *<-chan int, prime int) {
    for {
        i := <-in;  // Receive value of new variable 'i' from 'in'.
        if i % prime != 0 {
            out <- i  // Send 'i' to channel 'out'.
        }
    }
}

sieve.go, January 6, 2009 (cont'd)

// The prime sieve: Daisy-chain Filter processes together.
func Sieve() {
    ch := make(chan int);  // Create a new channel.
    go Generate(ch);       // Start Generate() as a subprocess.
    for {
        prime := <-ch;
        print(prime, "\n");
        ch1 := make(chan int);
        go Filter(ch, ch1, prime);
        ch = ch1
    }
}

func main() {
    Sieve()
}

sieve.go, September 25, 2009

First correct modern version. Also: capitalization gone. Uses fmt.

package main

import "fmt"

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func generate(ch chan<- int) {
    for i := 2; ; i++ {
        ch <- i;    // Send 'i' to channel 'ch'.
    }
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func filter(src <-chan int, dst chan<- int, prime int) {
    for i := range src {    // Loop over values received from 'src'.
        if i%prime != 0 {
            dst <- i;    // Send 'i' to channel 'dst'.
        }
    }
}
// The prime sieve: Daisy-chain filter processes together.
func sieve() {
	ch := make(chan int);  // Create a new channel.
	go generate(ch);       // Start generate() as a subprocess.
	for {
		prime := <-ch;
		fmt.Print(prime, "\n");
		ch1 := make(chan int);
		go filter(ch, ch1, prime);
		ch = ch1;
	}
}

func main() {
	sieve();
}

sieve.go, September 25, 2009 (cont'd)

// +build ignore,OMIT

package main

import "fmt"

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func generate(ch chan<- int) {
	for i := 2; ; i++ {
		ch <- i;	// Send 'i' to channel 'ch'.
	}
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func filter(src <-chan int, dst chan<- int, prime int) {
	for i := range src {	// Loop over values received from 'src'.
		if i%prime != 0 {
			dst <- i;	// Send 'i' to channel 'dst'.
		}
	}
}
// The prime sieve: Daisy-chain filter processes together.
func sieve() {
    ch := make(chan int);  // Create a new channel.
    go generate(ch);       // Start generate() as a subprocess.
    for {
        prime := <-ch;
        fmt.Print(prime, "\n");
        ch1 := make(chan int);
        go filter(ch, ch1, prime);
        ch = ch1;
    }
}

func main() {
    sieve();
}

sieve.go, December 10, 2009

Semicolons gone. Program as it is today.

package main

import "fmt"

// Send the sequence 2, 3, 4, … to channel 'ch'.
func generate(ch chan<- int) {
    for i := 2; ; i++ {
        ch <- i  // Send 'i' to channel 'ch'.
    }
}

// Copy the values from channel 'src' to channel 'dst',
// removing those divisible by 'prime'.
func filter(src <-chan int, dst chan<- int, prime int) {
    for i := range src {  // Loop over values received from 'src'.
        if i%prime != 0 {
            dst <- i  // Send 'i' to channel 'dst'.
        }
    }
}
// The prime sieve: Daisy-chain filter processes together.
func sieve() {
	ch := make(chan int)  // Create a new channel.
	go generate(ch)       // Start generate() as a subprocess.
	for {
		prime := <-ch
		fmt.Print(prime, "\n")
		ch1 := make(chan int)
		go filter(ch, ch1, prime)
		ch = ch1
	}
}

func main() {
	sieve()
}

sieve.go, December 10, 2009 (cont'd)

// +build ignore,OMIT

package main

import "fmt"

// Send the sequence 2, 3, 4, … to channel 'ch'.
func generate(ch chan<- int) {
	for i := 2; ; i++ {
		ch <- i  // Send 'i' to channel 'ch'.
	}
}

// Copy the values from channel 'src' to channel 'dst',
// removing those divisible by 'prime'.
func filter(src <-chan int, dst chan<- int, prime int) {
	for i := range src {  // Loop over values received from 'src'.
		if i%prime != 0 {
			dst <- i  // Send 'i' to channel 'dst'.
		}
	}
}
// The prime sieve: Daisy-chain filter processes together.
func sieve() {
    ch := make(chan int)  // Create a new channel.
    go generate(ch)       // Start generate() as a subprocess.
    for {
        prime := <-ch
        fmt.Print(prime, "\n")
        ch1 := make(chan int)
        go filter(ch, ch1, prime)
        ch = ch1
    }
}

func main() {
    sieve()
}

"This beautiful solution was contributed by a decades-long process of design."

Aside: Not discussed

The core connector for real concurrent applications. (A fact not always appreciated).
Origins in Dijkstra's guarded commands.
Made truly concurrent in Hoare's CSP.
Refined through Newsqueak, Alef, Limbo, and other routes.

Go's version specified on March 26, 2008.
Simplifications, clarifications, syntactic considerations.

Stability

Sieve program unchanged since late 2009—stability!

Open source systems are not always dependably compatible and stable.

Go is.

This is a very important reason for Go's success.

Trends

Graphs in usage metrics show knee in curve at Go 1.0 release.

Success

The factors for Go's success?

Obvious: Features and tools.

Success

Less obvious: process.

In short, an open source community that shares our mission,
coupled to a language designed for today's world.

Fitness to purpose

From Go: the emerging language of cloud infrastructure by Donnie Berkholz, March 2014.
golang.org/s/emerging

The future

This is where you come in!

Thank you