go.talks: changes to "code that grows with grace"

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/6774048
diff --git a/2012/chat.slide b/2012/chat.slide
index 97b2411..e723167 100644
--- a/2012/chat.slide
+++ b/2012/chat.slide
@@ -12,20 +12,6 @@
 
 It's my favorite language. I think you'll like it, too.
 
-* Why Go?
-
-Needed a language for building servers* at Google, with an emphasis on:
-
-- simplicity,
-- ease of use,
-- readability,
-- comprehensibility,
-- efficiency,
-- concurrency,
-- scale.
-
-(* Go has since proven to be a great general-purpose language.)
-
 * What is Go?
 
 An open source (BSD licensed) project:
@@ -39,10 +25,35 @@
 
 As of September 2012 we have more than 300 contributors.
 
-* Go has gophers
+* Go is about composition
+
+Go is Object Oriented, but not in the usual way.
+
+- no classes (methods may be declared on any type)
+- no subtype inheritance
+- interfaces are satisfied implicitly (structural typing)
+
+The result: simple pieces connected by small interfaces.
+
+* Go is about concurrency
+
+Go provides CSP-like concurrency primitives.
+
+- lightweight threads (goroutines)
+- typed thread-safe communication and synchronization (channels)
+
+The result: comprehensible concurrent code.
+
+* Go is about gophers
 
 .image chat/gophers.jpg
 
+* Core values
+
+Go is about composition, concurrency, and gophers.
+
+Keep that in mind.
+
 * Hello, go
 
 .play chat/support/hello.go
@@ -170,6 +181,11 @@
 
 .play chat/support/hello-web.go
 
+* Hello, WebSocket
+
+.code chat/support/websocket.js
+.play chat/support/websocket.go
+
 * Using the http and websocket packages
 
 .code chat/http/chat.go /package/,/^}/
@@ -185,17 +201,21 @@
 
 We can't just use a `websocket.Conn` instead of the `net.Conn`, because a `websocket.Conn` is held open by its handler function. Here we use a channel to keep the handler running until the socket's `Close` method is called.
 
-.code chat/http/chat.go /type.socket/,/^}/
-.code chat/http/chat.go /func.+socket.+Close/,/^}/
-.code chat/http/chat.go /func.socketHandler/,/^}/
-
-* Demo
+.code chat/http-noembed/chat.go /type.socket/,/END/
 
 * Struct embedding
 
-Go supports a kind of "mix-in" functionality with a feature known as "struct embedding". The embedding struct inherits the embedded type's methods.
+Go supports a kind of "mix-in" functionality with a feature known as "struct embedding". The embedding struct delegates calls to the embedded type's methods.
 
-.code chat/support/embed.go /type/,$
+.play chat/support/embed.go /type/,$
+
+* Embedding the websocket connection
+
+By embedding the `*websocket.Conn` as an `io.ReadWriter`, we can drop the explicit `socket` `Read` and `Write` methods. 
+
+.code chat/http/chat.go /type.socket/,/END/
+
+* Demo
 
 * Relieving loneliness
 
@@ -248,6 +268,15 @@
 
 * Demo
 
+* One more thing
+
+* TCP and HTTP at the same time
+
+.code chat/both/chat.go /func main/,/^}/
+.code chat/both/chat.go /func netListen/,/^}/
+
+* Demo
+
 * Discussion
 
 * Further reading
diff --git a/2012/chat/both/chat.go b/2012/chat/both/chat.go
new file mode 100644
index 0000000..9a9d702
--- /dev/null
+++ b/2012/chat/both/chat.go
@@ -0,0 +1,117 @@
+package main
+
+import (
+	"fmt"
+	"io"
+	"log"
+	"net"
+	"net/http"
+	"time"
+
+	"code.google.com/p/go.net/websocket"
+)
+
+const listenAddr = "localhost:4000"
+
+func main() {
+	go netListen() // HL
+	http.HandleFunc("/", rootHandler)
+	http.Handle("/socket", websocket.Handler(socketHandler))
+	err := http.ListenAndServe(listenAddr, nil)
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+func netListen() {
+	l, err := net.Listen("tcp", "localhost:4001")
+	if err != nil {
+		log.Fatal(err)
+	}
+	for {
+		c, err := l.Accept()
+		if err != nil {
+			log.Fatal(err)
+		}
+		go match(c)
+	}
+}
+
+type socket struct {
+	io.Reader
+	io.Writer
+	done chan bool
+}
+
+func (s socket) Close() error {
+	s.done <- true
+	return nil
+}
+
+var chain = NewChain(2) // 2-word prefixes
+
+func socketHandler(ws *websocket.Conn) {
+	r, w := io.Pipe()
+	go func() {
+		_, err := io.Copy(io.MultiWriter(w, chain), ws)
+		w.CloseWithError(err)
+	}()
+	s := socket{r, ws, make(chan bool)}
+	go match(s)
+	<-s.done
+}
+
+var partner = make(chan io.ReadWriteCloser)
+
+func match(c io.ReadWriteCloser) {
+	fmt.Fprint(c, "Waiting for a partner...")
+	select {
+	case partner <- c:
+		// now handled by the other goroutine
+	case p := <-partner:
+		chat(p, c)
+	case <-time.After(5 * time.Second):
+		chat(Bot(), c)
+	}
+}
+
+func chat(a, b io.ReadWriteCloser) {
+	fmt.Fprintln(a, "Found one! Say hi.")
+	fmt.Fprintln(b, "Found one! Say hi.")
+	errc := make(chan error, 1)
+	go cp(a, b, errc)
+	go cp(b, a, errc)
+	if err := <-errc; err != nil {
+		log.Println(err)
+	}
+	a.Close()
+	b.Close()
+}
+
+func cp(w io.Writer, r io.Reader, errc chan<- error) {
+	_, err := io.Copy(w, r)
+	errc <- err
+}
+
+// Bot returns an io.ReadWriteCloser that responds to
+// each incoming write with a generated sentence.
+func Bot() io.ReadWriteCloser {
+	r, out := io.Pipe() // for outgoing data
+	return bot{r, out}
+}
+
+type bot struct {
+	io.ReadCloser
+	out io.Writer
+}
+
+func (b bot) Write(buf []byte) (int, error) {
+	go b.speak()
+	return len(buf), nil
+}
+
+func (b bot) speak() {
+	time.Sleep(time.Second)
+	msg := chain.Generate(10) // at most 10 words
+	b.out.Write([]byte(msg))
+}
diff --git a/2012/chat/both/html.go b/2012/chat/both/html.go
new file mode 100644
index 0000000..7ab0779
--- /dev/null
+++ b/2012/chat/both/html.go
@@ -0,0 +1,66 @@
+package main
+
+import "html/template"
+import "net/http"
+
+func rootHandler(w http.ResponseWriter, r *http.Request) {
+	rootTemplate.Execute(w, listenAddr)
+}
+
+var rootTemplate = template.Must(template.New("root").Parse(`
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8" />
+<script>
+
+var input, output, websocket;
+
+function showMessage(m) {
+	var p = document.createElement("p");
+	p.innerHTML = m;
+	output.appendChild(p);
+}
+
+function onMessage(e) {
+	showMessage(e.data);
+}
+
+function onClose() {
+	showMessage("Connection closed.");
+}
+
+function sendMessage() {
+	var m = input.value;
+	input.value = "";
+	websocket.send(m + "\n");
+	showMessage(m);
+}
+
+function onKey(e) {
+	if (e.keyCode == 13) {
+		sendMessage();
+	}
+}
+
+function init() {
+	input = document.getElementById("input");
+	input.addEventListener("keyup", onKey, false);
+
+	output = document.getElementById("output");
+
+	websocket = new WebSocket("ws://{{.}}/socket");
+	websocket.onmessage = onMessage;
+	websocket.onclose = onClose;
+}
+
+window.addEventListener("load", init, false);
+
+</script>
+</head>
+<body>
+<input id="input" type="text">
+<div id="output"></div>
+</body>
+</html>
+`))
diff --git a/2012/chat/both/markov.go b/2012/chat/both/markov.go
new file mode 100644
index 0000000..6efee10
--- /dev/null
+++ b/2012/chat/both/markov.go
@@ -0,0 +1,79 @@
+package main
+
+// This Markov chain code is taken from the "Generating arbitrary text"
+// codewalk: http://golang.org/doc/codewalk/markov/
+
+import (
+	"bytes"
+	"fmt"
+	"math/rand"
+	"strings"
+	"sync"
+)
+
+// Prefix is a Markov chain prefix of one or more words.
+type Prefix []string
+
+// String returns the Prefix as a string (for use as a map key).
+func (p Prefix) String() string {
+	return strings.Join(p, " ")
+}
+
+// Shift removes the first word from the Prefix and appends the given word.
+func (p Prefix) Shift(word string) {
+	copy(p, p[1:])
+	p[len(p)-1] = word
+}
+
+// Chain contains a map ("chain") of prefixes to a list of suffixes.
+// A prefix is a string of prefixLen words joined with spaces.
+// A suffix is a single word. A prefix can have multiple suffixes.
+type Chain struct {
+	chain     map[string][]string
+	prefixLen int
+	mu        sync.Mutex
+}
+
+// NewChain returns a new Chain with prefixes of prefixLen words.
+func NewChain(prefixLen int) *Chain {
+	return &Chain{
+		chain:     make(map[string][]string),
+		prefixLen: prefixLen,
+	}
+}
+
+// Write parses the bytes into prefixes and suffixes that are stored in Chain.
+func (c *Chain) Write(b []byte) (int, error) {
+	br := bytes.NewReader(b)
+	p := make(Prefix, c.prefixLen)
+	for {
+		var s string
+		if _, err := fmt.Fscan(br, &s); err != nil {
+			break
+		}
+		key := p.String()
+		c.mu.Lock()
+		c.chain[key] = append(c.chain[key], s)
+		c.mu.Unlock()
+		p.Shift(s)
+	}
+	return len(b), nil
+}
+
+// Generate returns a string of at most n words generated from Chain.
+func (c *Chain) Generate(n int) string {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	p := make(Prefix, c.prefixLen)
+	var words []string
+	for i := 0; i < n; i++ {
+		choices := c.chain[p.String()]
+		if len(choices) == 0 {
+			break
+		}
+		next := choices[rand.Intn(len(choices))]
+		words = append(words, next)
+		p.Shift(next)
+	}
+	return strings.Join(words, " ")
+}
diff --git a/2012/chat/http-noembed/chat.go b/2012/chat/http-noembed/chat.go
new file mode 100644
index 0000000..06d3254
--- /dev/null
+++ b/2012/chat/http-noembed/chat.go
@@ -0,0 +1,72 @@
+package main
+
+import (
+	"fmt"
+	"io"
+	"log"
+	"net/http"
+
+	"code.google.com/p/go.net/websocket"
+)
+
+const listenAddr = "localhost:4000"
+
+func main() {
+	http.HandleFunc("/", rootHandler)
+	http.Handle("/socket", websocket.Handler(socketHandler))
+	err := http.ListenAndServe(listenAddr, nil)
+	if err != nil {
+		log.Fatal(err)
+	}
+}
+
+type socket struct {
+	conn *websocket.Conn
+	done chan bool
+}
+
+func (s socket) Read(b []byte) (int, error)  { return s.conn.Read(b) }
+func (s socket) Write(b []byte) (int, error) { return s.conn.Write(b) }
+
+func (s socket) Close() error {
+	s.done <- true
+	return nil
+}
+
+func socketHandler(ws *websocket.Conn) {
+	s := socket{conn: ws, done: make(chan bool)}
+	go match(s)
+	<-s.done
+}
+
+// END OMIT
+
+var partner = make(chan io.ReadWriteCloser)
+
+func match(c io.ReadWriteCloser) {
+	fmt.Fprint(c, "Waiting for a partner...")
+	select {
+	case partner <- c:
+		// now handled by the other goroutine
+	case p := <-partner:
+		chat(p, c)
+	}
+}
+
+func chat(a, b io.ReadWriteCloser) {
+	fmt.Fprintln(a, "Found one! Say hi.")
+	fmt.Fprintln(b, "Found one! Say hi.")
+	errc := make(chan error, 1)
+	go cp(a, b, errc)
+	go cp(b, a, errc)
+	if err := <-errc; err != nil {
+		log.Println(err)
+	}
+	a.Close()
+	b.Close()
+}
+
+func cp(w io.Writer, r io.Reader, errc chan<- error) {
+	_, err := io.Copy(w, r)
+	errc <- err
+}
diff --git a/2012/chat/http-noembed/html.go b/2012/chat/http-noembed/html.go
new file mode 100644
index 0000000..2e54af2
--- /dev/null
+++ b/2012/chat/http-noembed/html.go
@@ -0,0 +1,66 @@
+package main
+
+import "html/template"
+import "net/http"
+
+func rootHandler(w http.ResponseWriter, r *http.Request) {
+	rootTemplate.Execute(w, listenAddr)
+}
+
+var rootTemplate = template.Must(template.New("root").Parse(`
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8" />
+<script>
+
+var input, output, websocket;
+
+function showMessage(m) {
+	var p = document.createElement("p");
+	p.innerHTML = m;
+	output.appendChild(p);
+}
+
+function onMessage(e) {
+	showMessage(e.data);
+}
+
+function onClose() {
+	showMessage("Connection closed.");
+}
+
+function sendMessage() {
+	var m = input.value;
+	input.value = "";
+	websocket.send(m);
+	showMessage(m);
+}
+
+function onKey(e) {
+	if (e.keyCode == 13) {
+		sendMessage();
+	}
+}
+
+function init() {
+	input = document.getElementById("input");
+	input.addEventListener("keyup", onKey, false);
+
+	output = document.getElementById("output");
+
+	websocket = new WebSocket("ws://{{.}}/socket");
+	websocket.onmessage = onMessage;
+	websocket.onclose = onClose;
+}
+
+window.addEventListener("load", init, false);
+
+</script>
+</head>
+<body>
+<input id="input" type="text">
+<div id="output"></div>
+</body>
+</html>
+`))
diff --git a/2012/chat/http/chat.go b/2012/chat/http/chat.go
index 31938b5..3ff0e9f 100644
--- a/2012/chat/http/chat.go
+++ b/2012/chat/http/chat.go
@@ -21,7 +21,7 @@
 }
 
 type socket struct {
-	io.ReadWriter
+	io.ReadWriter // HL
 	done chan bool
 }
 
@@ -31,11 +31,13 @@
 }
 
 func socketHandler(ws *websocket.Conn) {
-	s := socket{ws, make(chan bool)}
+	s := socket{ws, make(chan bool)} // HL
 	go match(s)
 	<-s.done
 }
 
+// END OMIT
+
 var partner = make(chan io.ReadWriteCloser)
 
 func match(c io.ReadWriteCloser) {
diff --git a/2012/chat/markov/chat.go b/2012/chat/markov/chat.go
index 6e64485..05b0436 100644
--- a/2012/chat/markov/chat.go
+++ b/2012/chat/markov/chat.go
@@ -54,7 +54,7 @@
 		// now handled by the other goroutine
 	case p := <-partner:
 		chat(p, c)
-	case <-time.After(10 * time.Second): // HL
+	case <-time.After(5 * time.Second): // HL
 		chat(Bot(), c) // HL
 	}
 }
diff --git a/2012/chat/support/embed.go b/2012/chat/support/embed.go
index d7725c8..6dc0033 100644
--- a/2012/chat/support/embed.go
+++ b/2012/chat/support/embed.go
@@ -2,17 +2,19 @@
 
 import "fmt"
 
-type A struct {
-	B
+type A struct{}
+
+func (A) Hello() {
+	fmt.Println("Hello!")
 }
 
-type B struct{}
-
-func (b B) String() string {
-	return "B comes after A"
+type B struct {
+	A
 }
 
+// func (b B) Hello() { b.A.Hello() } // (implicitly!)
+
 func main() {
-	var a A
-	fmt.Println(a) // Println calls String to format a
+	var b B
+	b.Hello()
 }
diff --git a/2012/chat/support/websocket.go b/2012/chat/support/websocket.go
new file mode 100644
index 0000000..6d1547d
--- /dev/null
+++ b/2012/chat/support/websocket.go
@@ -0,0 +1,19 @@
+package main
+
+import (
+	"code.google.com/p/go.net/websocket"
+	"fmt"
+	"net/http"
+)
+
+func main() {
+	http.Handle("/", websocket.Handler(handler))
+	http.ListenAndServe("localhost:4000", nil)
+}
+
+func handler(c *websocket.Conn) {
+	var s string
+	fmt.Fscan(c, &s)
+	fmt.Println("Received:", s)
+	fmt.Fprint(c, "How do you do?")
+}
diff --git a/2012/chat/support/websocket.js b/2012/chat/support/websocket.js
new file mode 100644
index 0000000..f9809d6
--- /dev/null
+++ b/2012/chat/support/websocket.js
@@ -0,0 +1,3 @@
+var sock = new WebSocket("ws://localhost:4000/");
+sock.onmessage = function(m) { console.log("Received:", m.data); }
+sock.send("Hello!\n")