Hello, Gophers! (original) (raw)
Gophercon Opening Keynote
24 April 2014
Video
A video of this talk was recorded at GopherCon in Denver.
2
Hello, gophers!
3
Hello, gophers!
package main
import "fmt"
func main() { fmt.Printf("Hello, gophers!\n") }
4
History
This is a historic occasion.
Go has achieved a level of success worthy of a conference.
5
Success
Many factors contribute to that success.
- features
- lack of features
- combination of features
- design
- people
- time 6
Case study
A look back, focusing on code.
7
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".
8
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.)
9
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.
10
hello.c
main() { printf("hello, world\n"); }
First appeared in
The C Programming Language, by Brian W. Kernighan and Dennis M. Ritchie, 1978.
11
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.
12
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
13
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.
14
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
.
15
hello.go, June 27, 2008
package main
func main() { print "hello, world\n"; }
When main
returns, the program calls exit(0)
.
16
hello.go, August 11, 2008
package main
func main() { print("hello, world\n"); }
Parentheses now required: print
now a function not a primitive.
17
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.)
18
hello.go, January 15, 2009
package main
import "fmt"
func main() { fmt.Printf("hello, world\n"); }
Upper case for export. "Casification."
19
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.
20
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, ...
21
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.
22
Hello, world in 16 tokens
package
main
import
"fmt"
func
main
(
)
{
fmt
.
Println
(
"Hello, Gophers (some of whom know 日本語)!"
)
}
23
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.
24
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.
25
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.
26
"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.
27
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
.
28
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'"
29
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.
30
()
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.
31
{
Braces not spaces.
And not square brackets.
Why is the newline after the brace?
32
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
?
33
.
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).
34
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.
35
(
Traditional function syntax.
36
"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.
37
)
No semicolon.
Semicolons went away shortly after release.
Much futzing around to try to cull them in early days.
Eventually accepted the BCPL approach.
38
}
Done.
39
Aside: Not discussed
- types
- constants
- methods
- interfaces
- libraries
- memory management
- concurrency (coming up)
Plus tools, ecosystem, community, ...:
Language is central but only part of the story.
40
Success
Factors:
- building on history
- building on experience
- process of design
- early ideas refined into final approach
- concentrated effort by a small dedicated team
Finally: Commitment.
Go 1.0 locked down the language and libraries.
41
Another round
Now watch similar evolution of a second program.
42
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. "
43
Solution
Defined in the 1978 CSP paper.
(Note: not the sieve of Eratosthenes.)
"This beautiful solution was contributed by David Gries."
44
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.
45
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; };
46
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); "";
47
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'. } } }
48
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(); }
49
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'. } } }
50
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() }
51
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'. } } }
52
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() }
53
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'. } } }
54
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() }
55
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(); }
56
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(); }
57
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() }
58
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."
59
Aside: Not discussed
select
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.
60
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.
61
Trends
Graphs in usage metrics show knee in curve at Go 1.0 release.
62
Success
The factors for Go's success?
Obvious: Features and tools.
- concurrency
- garbage collection
- efficient implementation
- static types but dynamic feel
- rich but limited standard library
- tooling (and the factors that make it possible)
gofmt
- programming in the large 63
Success
Less obvious: process.
- focus on the original goals
- concentrated development followed by freeze
- consensus of a small core team
- vital contributions from a community that "gets it"
- rich ecosystem generated as a consequence
In short, an open source community that shares our mission,
coupled to a language designed for today's world.
64
Fitness to purpose
From Go: the emerging language of cloud infrastructure by Donnie Berkholz, March 2014.
</s/emerging>
65
The future
This is where you come in!
66
Thank you
Use the left and right arrow keys or click the left and right edges of the page to navigate between slides.
(Press 'H' or navigate to hide this message.)