talks: state of go 2016

Change-Id: I4c7f1dedbab624f08fef4142765ea094c488311d
Reviewed-on: https://go-review.googlesource.com/19121
Reviewed-by: Andrew Gerrand <adg@golang.org>
diff --git a/2016/state-of-go.slide b/2016/state-of-go.slide
new file mode 100644
index 0000000..fa48bba
--- /dev/null
+++ b/2016/state-of-go.slide
@@ -0,0 +1,509 @@
+The State of Go
+Where we are in February 2016
+
+Francesc Campoy
+Gopher at Google
+@francesc
+campoy@golang.org
+
+* Time flies
+
+Go 1.4 is one year old (happy birthday!)
+
+Go 1.5 is already 6 months old!
+
+Go 1.6 to be released sometime in February.
+
+Go 1.6 Candidate Release 1 was released on January 28th
+
+* Notes
+
+The slides are available on [[https://talks.golang.org/2016/state-of-go.slide]]
+
+Most of the code examples won't run except locally and using Go 1.6.
+
+The playground still runs Go 1.5.
+
+* Agenda
+
+Changes since Go 1.5:
+
+- the language
+- the standard library
+- the runtime
+- the tooling
+- the community
+
+* Changes to the language
+
+* Changes to the language
+
+None.
+
+This is a feature.
+
+* Changes to the standard library
+
+* Changes to the standard library
+
+A couple of new things, in order of excitement.
+
+- net/http
+
+- {text,html}/template
+
+- sort
+
+Also more speed and fewer bugs.
+
+* net/http
+
+If:
+
+- you're using HTTPS
+
+- and Go 1.6
+
+you're using HTTP/2!
+
+* HTTP/2
+
+At a high level, HTTP/2:
+
+- is binary, instead of textual
+- is fully multiplexed, instead of ordered and blocking
+- can therefore use one connection for parallelism
+- uses header compression to reduce overhead
+- allows servers to “push” responses proactively into client caches
+
+.link https://http2.golang.org/gophertiles
+
+* text/template
+
+Imagine that given a slice of strings:
+
+	[]string{"one", "two", "three"}
+
+We want to write a template that will generate:
+
+	<ul>
+	<li>one</li>
+	<li>two</li>
+	<li>three</li>
+	</ul>
+
+What template would you write?
+
+* text/template: first try
+
+Naturally, I write this:
+
+.play state-of-go/template/old.go /`/,/`/
+
+But unfortunately it's not exactly what I want!
+
+* text/template: let's fix it
+
+We need to be careful with the line breaks.
+
+.play state-of-go/template/fixed.go /`/,/`/
+
+This works now, but ... I don't really like my code!
+
+* text/template: meet {{- and -}}
+
+Go 1.6 brings two new delimiters:
+
+- `{{-`
+- `-}}`
+
+Similar to `{{` and `}}`, but _all_ white space on the `-` side will be trimmed.
+
+The template:
+
+	{{23 -}}
+	<
+	{{- 45}}
+
+generates:
+
+	23 < 45
+
+* text/template: back to our problem
+
+We can now have:
+
+- the result we expected
+- without sacrificing the readibility of our templates.
+
+.play state-of-go/template/new.go /`/,/`/
+
+* text/template: the block action
+
+Go 1.6 brings also a new action named `block`.
+
+Let's see what it is useful for.
+
+* Factoring out repetition in templates
+
+Both `<ul>` share the same structure.
+
+	{{define "presentation"}}
+		Authors:
+		<ul>
+		{{range .Authors}}
+			<li>{{.}}</li>
+		{{end}}
+		</ul>
+
+		Topics:
+		<ul>
+		{{range .Topics}}
+			<li>{{.}}</li>
+		{{end}}
+		</ul>
+	{{end}}
+
+Templates can be used to avoid repetition.
+
+* Factoring out repetition with templates (cont.)
+
+We can define a new template:
+
+.code state-of-go/template/define.go /define "list"/,/^{{end}}/
+
+And use it where needed:
+
+.code state-of-go/template/define.go /define "presentation"/,/^{{end}}/
+
+* Factoring out repetition with templates (cont.)
+
+We can parse that template and execute it.
+
+.play state-of-go/template/define.go /func main/,/^}/
+
+* Template redefinition
+
+We made also our template easier to reuse, as we can redefine `list`.
+
+.play state-of-go/template/redefine.go /func main/,/^}/
+
+* Meet the block action
+
+The `block` action defines and executes a template in place.
+
+.code state-of-go/template/blocks.go /define/,/`/
+
+That template defined by `block` can be:
+
+- referenced later by the same template,
+- redefined with `define`.
+
+* Why do we need it?
+
+It is more compact when
+
+- we're not factoring out repetition,
+- but we need to provide an extension point.
+
+We can make the following template more compact with `block`.
+
+	{{define "content" .}}
+		<h1>{{.Heading}}<h1>
+		<p>{{.Content}}</p>
+	{{end}}
+
+	{{define "page"}}
+		<title>{{.Title}}</title>
+		<body>
+		{{template "content" .}}
+		</body>
+	{{end}}
+
+* Why do we need it? (cont.)
+
+We can make the following template more compact with `block`.
+
+	{{define "page"}}
+		<title>{{.Title}}</title>
+		<body>
+		{{block "content" .}}
+			<h1>{{.Heading}}<h1>
+			<p>{{.Content}}</p>
+		{{end}}
+		</body>
+	{{end}}
+
+And still easily redefine `content`.
+
+* sort.Sort is faster
+
+`Sort` sorts your data by calling `Less`, `Swap`, and `Len`.
+
+We reduced the number of comparisons and swaps by about 10%.
+
+Sort `[]int` with Go 1.5
+
+	BenchmarkSort_1-4       20000000              67.2 ns/op
+	BenchmarkSort_10-4      10000000               227 ns/op
+	BenchmarkSort_100-4       500000              3863 ns/op
+	BenchmarkSort_1000-4       30000             52189 ns/op
+
+Sort `[]int` with Go 1.6
+
+	BenchmarkSort_1-4       20000000              64.7 ns/op
+	BenchmarkSort_10-4      10000000               137 ns/op
+	BenchmarkSort_100-4       500000              2849 ns/op
+	BenchmarkSort_1000-4       30000             46949 ns/op
+
+* sort.Sort is faster - plot
+
+.image state-of-go/img/bench-sort.png _ 800
+
+* Sort order and sort.Stable
+
+Reminder: sort.Sort is not a _stable_ sort.
+
+.play state-of-go/sort/unstable.go /byLength/,
+
+Use `sort.Stable`:
+
+.play state-of-go/sort/stable.go /func main/,
+
+* Minor changes
+
+.image state-of-go/img/minorchanges.png _ 900
+.caption too many to discuss: find them [[https://golang.org/doc/go1.6#minor_library_changes][here]]
+
+* Let's just discuss one
+
+`time.Parse` is smarter!
+
+.play state-of-go/time/time.go /func main/,/^}/
+
+
+* Changes to the runtime
+
+* Detection of concurrent map accesses
+
+Detection of unsafe concurrent access to maps.
+
+.play state-of-go/runtime/crash.go /const/,/Wait\(\)/
+
+Outputs:
+
+	fatal error: concurrent map read and map write
+	fatal error: concurrent map writes
+
+* Does it make it slower?
+
+No!
+
+Let's benchmark it - with a correct solution.
+
+.code state-of-go/runtime/good/good.go /func count/,/^}/
+
+* Benchmark results
+
+Go 1.4 - GOMAXPROCS = 4
+
+	BenchmarkCount_1         1000000              1862 ns/op
+	BenchmarkCount_10         100000             21214 ns/op
+	BenchmarkCount_100          1000           1602507 ns/op
+	BenchmarkCount_1000           10         141712948 ns/op
+
+Go 1.5 - GOMAXPROCS = 4
+
+	BenchmarkCount_1-4       2000000               867 ns/op
+	BenchmarkCount_10-4       200000              6909 ns/op
+	BenchmarkCount_100-4        1000           1025092 ns/op
+	BenchmarkCount_1000-4         20          94093719 ns/op
+
+Go 1.6 - GOMAXPROCS = 4
+
+	BenchmarkCount_1-4       2000000               750 ns/op
+	BenchmarkCount_10-4       200000              6582 ns/op
+	BenchmarkCount_100-4        2000           1113790 ns/op
+	BenchmarkCount_1000-4         20          87998054 ns/op
+
+* Benchmark results plot
+
+.image state-of-go/img/bench4.png _ 800
+
+* Benchmark results
+
+Go 1.4 - GOMAXPROCS = 1
+
+	BenchmarkCount_1         100000               1370 ns/op
+	BenchmarkCount_10         20000               8622 ns/op
+	BenchmarkCount_100          500             362725 ns/op
+	BenchmarkCount_1000          50           31378803 ns/op
+
+Go 1.5 - GOMAXPROCS = 1
+
+	BenchmarkCount_1-4      2000000                776 ns/op
+	BenchmarkCount_10-4      200000               6288 ns/op
+	BenchmarkCount_100-4       3000             345037 ns/op
+	BenchmarkCount_1000-4        50           31751625 ns/op
+
+Go 1.6 - GOMAXPROCS = 1
+
+	BenchmarkCount_1-4       2000000               767 ns/op
+	BenchmarkCount_10-4       200000              6041 ns/op
+	BenchmarkCount_100-4        5000            329328 ns/op
+	BenchmarkCount_1000-4         50          30176034 ns/op
+
+* Benchmark results plot
+
+.image state-of-go/img/bench1.png _ 800
+
+* Garbage Collector in Go 1.5
+
+At [[https://www.youtube.com/watch?v=aiv1JOfMjm0][GopherCon 2015 Rick Hudson gave a presentation]] about the Go 1.5 low latency collector
+
+.image state-of-go/img/gc345.png 500 _
+
+* Garbage Collector in Go 1.6
+
+At [[https://www.infoq.com/presentations/go-gc-performance][QCon SF in November Rick Hudson]] gave an updated presentation which showed this comparison of Go 1.5 to the upcoming Go 1.6
+
+.image state-of-go/img/gc56.png 400 _
+
+_Yes,_that_is_gigabytes_on_the_X_axis_
+
+* Garbage Collector on tip
+
+Right now it's even better!
+
+.image state-of-go/img/gcgotip.png _ 600
+
+* People loved it with Go 1.5
+
+.image state-of-go/img/twitter1.png _ 550
+.caption [[https://twitter.com/brianhatfield/status/634166123605331968][original tweet]]
+
+* People love it even more with Go 1.6
+
+.image state-of-go/img/twitter2.png _ 600
+.caption [[https://twitter.com/brianhatfield/status/692778741567721473][original tweet]]
+
+* Oops
+
+.image state-of-go/img/twitter3.png _ 600
+.caption [[https://twitter.com/bradfitz/status/692787593558118400][original tweet]] and [[https://www.youtube.com/watch?v=CduA0TULnow&t=1m29s][video]]
+
+* New ports
+
+Experimental ports to Linux on 64-bit MIPS (linux/mips64 and linux/mips64le).
+
+Experimental port to Android on 32-bit x86 (android/386).
+
+On Linux on little-endian 64-bit PowerPC (linux/ppc64le), Go 1.6 now supports cgo with external linking and is roughly feature complete.
+
+On NaCl, Go 1.5 required SDK version pepper-41. Go 1.6 adds support for later SDK versions.
+
+* Changes to the tooling
+
+* The cgo tool
+
+Go is garbage collected, can C and Go share memory?
+
+In short:
+
+- Go can pass a pointer to C
+- the referenced memory can't have pointers to Go allocated memory
+- C can't keep pointers to the memory after the call returns
+
+In more detail:
+
+- the `cgo` [[https://tip.golang.org/cmd/cgo/#hdr-Passing_pointers][docs]].
+
+This is checked by the runtime at execution.
+
+You could disable the checks, but you probably shouldn't.
+
+* Sharing pointers between Go and C
+
+.code state-of-go/cgo/main.go
+
+Outputs:
+
+	panic: runtime error: cgo argument has Go pointer to Go pointer
+
+* The go tool
+
+GO15VENDOREXPERIMENT is now enabled by default.
+
+How does it work?
+
+	/home/user/gocode/
+	    src/
+	        server-one/
+	            main.go            (import "github.com/gorilla/mux")
+	        server-two/
+	            main.go            (import "github.com/gorilla/mux")
+	            vendor/
+	                github.com/
+	                    gorilla/
+	                        mux/
+	                            ...
+
+`server-one` uses the `mux` package in `$GOPATH/src/github.com/gorilla/mux`.
+
+`server-two` uses the `mux` package in `vendor`.
+
+* Vendoring demo
+
+* go doc
+
+Go 1.5 added the possibility of searching by name
+
+	go doc math Pi
+
+Go 1.6 defines priority of packages with import paths with less elements.
+
+Non vendored packages appear first.
+
+* go vet
+
+Go vet warns if the code prints a function instead of its result.
+
+.play state-of-go/govet/main.go
+
+`go`vet` output:
+
+	main.go:8: arg foo in Println call is a function value, not a function call
+
+The warning can be removed using `Printf` and the `%v` or `%p` formats.
+
+* The community
+
+* The community
+
+Code of Conduct announced on November 24th 2015
+
+Go meetups:
+
+- [[http://go-meetups.appspot.com]]
+
+Women Who Go - 7 chapters already!
+
+- [[http://www.womenwhogo.org]]
+
+* Conferences:
+
+- [[http://gophergala.com/][GopherGala]] Jan 22-24th (judging proposals now)
+- [[https://fosdem.org/2016/][FOSDEM]] Right here, right now. Hello!
+- [[http://gophercon.ae][GopherCon Dubai]], Feb 23rd
+- [[http://www.gophercon.in/][GopherCon India]], Feb 19-20th
+- [[https://gophercon.com/][Gophercon Denver]], July 11-13th
+- [[http://2016.dotgo.eu/][dotGo]], Oct 10th
+
+* Go 1.6 release party, February 17th
+
+Go 1.6 ships soon!
+
+Go meetups are organising to hold a release party on the 17th of February.
+
+.image state-of-go/img/party-gopher.png _ 300
+.caption Join the party!!!
diff --git a/2016/state-of-go/cgo/main.go b/2016/state-of-go/cgo/main.go
new file mode 100644
index 0000000..1d83eb2
--- /dev/null
+++ b/2016/state-of-go/cgo/main.go
@@ -0,0 +1,20 @@
+// +build OMIT
+
+package main
+
+/*
+int fn(void* arg) { return arg == 0; }
+*/
+import "C"
+import "unsafe"
+
+type T struct{ a, b int }
+type X struct{ t *T }
+
+func main() {
+	t := T{a: 1, b: 2}
+	C.fn(unsafe.Pointer(&t)) // correct // HL
+
+	x := X{t: &t}
+	C.fn(unsafe.Pointer(&x)) // incorrect // HL
+}
diff --git a/2016/state-of-go/govet/main.go b/2016/state-of-go/govet/main.go
new file mode 100644
index 0000000..4f361bd
--- /dev/null
+++ b/2016/state-of-go/govet/main.go
@@ -0,0 +1,11 @@
+// +build OMIT
+
+package main
+
+import "fmt"
+
+func foo() string { return "bar" }
+
+func main() {
+	fmt.Printf("%v", foo)
+}
diff --git a/2016/state-of-go/img/bench-sort.png b/2016/state-of-go/img/bench-sort.png
new file mode 100644
index 0000000..e42d4a2
--- /dev/null
+++ b/2016/state-of-go/img/bench-sort.png
Binary files differ
diff --git a/2016/state-of-go/img/bench1.png b/2016/state-of-go/img/bench1.png
new file mode 100644
index 0000000..0fee88c
--- /dev/null
+++ b/2016/state-of-go/img/bench1.png
Binary files differ
diff --git a/2016/state-of-go/img/bench4.png b/2016/state-of-go/img/bench4.png
new file mode 100644
index 0000000..88cd3d1
--- /dev/null
+++ b/2016/state-of-go/img/bench4.png
Binary files differ
diff --git a/2016/state-of-go/img/gc345.png b/2016/state-of-go/img/gc345.png
new file mode 100644
index 0000000..3180473
--- /dev/null
+++ b/2016/state-of-go/img/gc345.png
Binary files differ
diff --git a/2016/state-of-go/img/gc56.png b/2016/state-of-go/img/gc56.png
new file mode 100644
index 0000000..bc97b2d
--- /dev/null
+++ b/2016/state-of-go/img/gc56.png
Binary files differ
diff --git a/2016/state-of-go/img/gcgotip.png b/2016/state-of-go/img/gcgotip.png
new file mode 100644
index 0000000..7088e1c
--- /dev/null
+++ b/2016/state-of-go/img/gcgotip.png
Binary files differ
diff --git a/2016/state-of-go/img/minorchanges.png b/2016/state-of-go/img/minorchanges.png
new file mode 100644
index 0000000..3a22ee6
--- /dev/null
+++ b/2016/state-of-go/img/minorchanges.png
Binary files differ
diff --git a/2016/state-of-go/img/party-gopher.png b/2016/state-of-go/img/party-gopher.png
new file mode 100644
index 0000000..d18690f
--- /dev/null
+++ b/2016/state-of-go/img/party-gopher.png
Binary files differ
diff --git a/2016/state-of-go/img/twitter1.png b/2016/state-of-go/img/twitter1.png
new file mode 100644
index 0000000..1825bb9
--- /dev/null
+++ b/2016/state-of-go/img/twitter1.png
Binary files differ
diff --git a/2016/state-of-go/img/twitter2.png b/2016/state-of-go/img/twitter2.png
new file mode 100644
index 0000000..81ce982
--- /dev/null
+++ b/2016/state-of-go/img/twitter2.png
Binary files differ
diff --git a/2016/state-of-go/img/twitter3.png b/2016/state-of-go/img/twitter3.png
new file mode 100644
index 0000000..9368027
--- /dev/null
+++ b/2016/state-of-go/img/twitter3.png
Binary files differ
diff --git a/2016/state-of-go/runtime/crash.go b/2016/state-of-go/runtime/crash.go
new file mode 100644
index 0000000..60a07bb
--- /dev/null
+++ b/2016/state-of-go/runtime/crash.go
@@ -0,0 +1,26 @@
+/// +build OMIT
+
+package main
+
+import (
+	"fmt"
+	"sync"
+)
+
+func main() {
+	const workers = 100 // what if we have 1, 2, 25?
+
+	var wg sync.WaitGroup
+	wg.Add(workers)
+	m := map[int]int{}
+	for i := 1; i <= workers; i++ {
+		go func(i int) {
+			for j := 0; j < i; j++ {
+				m[i]++ // HL
+			}
+			wg.Done()
+		}(i)
+	}
+	wg.Wait()
+	fmt.Println(m)
+}
diff --git a/2016/state-of-go/runtime/good/good.go b/2016/state-of-go/runtime/good/good.go
new file mode 100644
index 0000000..830cc8e
--- /dev/null
+++ b/2016/state-of-go/runtime/good/good.go
@@ -0,0 +1,31 @@
+// +build OMIT
+
+package main
+
+import (
+	"runtime"
+	"sync"
+)
+
+func main() {
+	runtime.GOMAXPROCS(runtime.NumCPU())
+	count(10000) // what if we have 1, 2, 25?
+}
+
+func count(n int) {
+	var wg sync.WaitGroup
+	wg.Add(n)
+	m := map[int]int{}
+	var mu sync.Mutex // HL
+	for i := 1; i <= n; i++ {
+		go func(i int) {
+			for j := 0; j < i; j++ {
+				mu.Lock() // HL
+				m[i]++
+				mu.Unlock() // HL
+			}
+			wg.Done()
+		}(i)
+	}
+	wg.Wait()
+}
diff --git a/2016/state-of-go/sort/stable.go b/2016/state-of-go/sort/stable.go
new file mode 100644
index 0000000..74041a9
--- /dev/null
+++ b/2016/state-of-go/sort/stable.go
@@ -0,0 +1,21 @@
+// +build OMIT
+
+package main
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+)
+
+type byLength []string
+
+func (b byLength) Len() int           { return len(b) }
+func (b byLength) Less(i, j int) bool { return len(b[i]) < len(b[j]) }
+func (b byLength) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
+
+func main() {
+	values := []string{"ball", "hell", "one", "joke", "fool", "moon", "two"}
+	sort.Stable(byLength(values)) // HL
+	fmt.Println(strings.Join(values, "\n"))
+}
diff --git a/2016/state-of-go/sort/unstable.go b/2016/state-of-go/sort/unstable.go
new file mode 100644
index 0000000..3923a2c
--- /dev/null
+++ b/2016/state-of-go/sort/unstable.go
@@ -0,0 +1,21 @@
+// +build OMIT
+
+package main
+
+import (
+	"fmt"
+	"sort"
+	"strings"
+)
+
+type byLength []string
+
+func (b byLength) Len() int           { return len(b) }
+func (b byLength) Less(i, j int) bool { return len(b[i]) < len(b[j]) }
+func (b byLength) Swap(i, j int)      { b[i], b[j] = b[j], b[i] }
+
+func main() {
+	values := []string{"ball", "hell", "one", "joke", "fool", "moon", "two"}
+	sort.Sort(byLength(values))
+	fmt.Println(strings.Join(values, "\n"))
+}
diff --git a/2016/state-of-go/template/blocks.go b/2016/state-of-go/template/blocks.go
new file mode 100644
index 0000000..c549ee6
--- /dev/null
+++ b/2016/state-of-go/template/blocks.go
@@ -0,0 +1,48 @@
+// +build OMIT
+
+package main
+
+import (
+	"html/template"
+	"log"
+	"os"
+	"strings"
+)
+
+const tmplText = `
+{{define "presentation"}}
+	Authors:
+	{{block "list" .Authors}} // HL
+	<ul>
+	{{- range .}}
+		<li>{{.}}</li>
+	{{- end}}
+	</ul>
+	{{end}} // HL
+
+	Topics:
+	{{template "list" .Topics}} // HL
+{{end}}
+` // OMIT
+
+type Presentation struct {
+	Authors []string
+	Topics  []string
+}
+
+func main() {
+	p := Presentation{
+		Authors: []string{"one", "two", "three"},
+		Topics:  []string{"go", "templates"},
+	}
+
+	tmpl := template.Must(template.New("presentation").Parse(tmplText))
+
+	tmpl = tmpl.Funcs(template.FuncMap{"join": strings.Join})                       // HL
+	tmpl = template.Must(tmpl.Parse(`{{define "list"}} {{join . " | "}} {{ end}}`)) // HL
+
+	err := tmpl.Execute(os.Stdout, p)
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/2016/state-of-go/template/define.go b/2016/state-of-go/template/define.go
new file mode 100644
index 0000000..046f493
--- /dev/null
+++ b/2016/state-of-go/template/define.go
@@ -0,0 +1,46 @@
+// +build OMIT
+
+package main
+
+import (
+	"html/template"
+	"log"
+	"os"
+)
+
+const tmplText = `
+{{define "list"}}
+	<ul>
+	{{range .}}
+		<li>{{.}}</li>
+	{{end}}
+	</ul>
+{{end}}	
+
+{{define "presentation"}}
+	Authors:
+	{{template "list" .Authors}}
+
+	Topics:
+	{{template "list" .Topics}}
+{{end}}
+`
+
+type Presentation struct {
+	Authors []string
+	Topics  []string
+}
+
+func main() {
+	p := Presentation{
+		Authors: []string{"one", "two", "three"},
+		Topics:  []string{"go", "templates"},
+	}
+
+	tmpl := template.Must(template.New("presentation").Parse(tmplText)) // HL
+
+	err := tmpl.Execute(os.Stdout, p)
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/2016/state-of-go/template/fixed.go b/2016/state-of-go/template/fixed.go
new file mode 100644
index 0000000..ddda356
--- /dev/null
+++ b/2016/state-of-go/template/fixed.go
@@ -0,0 +1,22 @@
+// +build OMIT
+
+package main
+
+import (
+	"html/template"
+	"log"
+	"os"
+)
+
+var tmpl = template.Must(template.New("tmpl").Parse(`
+<ul>
+{{range .}}<li>{{.}}</li>
+{{end}}</ul>
+`))
+
+func main() {
+	err := tmpl.Execute(os.Stdout, []string{"one", "two", "three"})
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/2016/state-of-go/template/new.go b/2016/state-of-go/template/new.go
new file mode 100644
index 0000000..fc5d545
--- /dev/null
+++ b/2016/state-of-go/template/new.go
@@ -0,0 +1,24 @@
+// +build OMIT
+
+package main
+
+import (
+	"html/template"
+	"log"
+	"os"
+)
+
+var tmpl = template.Must(template.New("tmpl").Parse(`
+<ul>
+{{range . -}}
+	<li>{{.}}</li>
+{{end -}}
+</ul>
+`))
+
+func main() {
+	err := tmpl.Execute(os.Stdout, []string{"one", "two", "three"})
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/2016/state-of-go/template/old.go b/2016/state-of-go/template/old.go
new file mode 100644
index 0000000..f5156c4
--- /dev/null
+++ b/2016/state-of-go/template/old.go
@@ -0,0 +1,24 @@
+// +build OMIT
+
+package main
+
+import (
+	"html/template"
+	"log"
+	"os"
+)
+
+var tmpl = template.Must(template.New("tmpl").Parse(`
+<ul>
+{{range .}}
+	<li>{{.}}</li>
+{{end}}
+</ul>
+`))
+
+func main() {
+	err := tmpl.Execute(os.Stdout, []string{"one", "two", "three"})
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/2016/state-of-go/template/redefine.go b/2016/state-of-go/template/redefine.go
new file mode 100644
index 0000000..264768c
--- /dev/null
+++ b/2016/state-of-go/template/redefine.go
@@ -0,0 +1,50 @@
+// +build OMIT
+
+package main
+
+import (
+	"html/template"
+	"log"
+	"os"
+	"strings"
+)
+
+const tmplText = `
+{{define "list"}}
+	<ul>
+	{{range .}}
+		<li>{{.}}</li>
+	{{end}}
+	</ul>
+{{end}}	
+
+{{define "presentation"}}
+	Authors:
+	{{template "list" .Authors}}
+
+	Topics:
+	{{template "list" .Topics}}
+{{end}}
+`
+
+type Presentation struct {
+	Authors []string
+	Topics  []string
+}
+
+func main() {
+	p := Presentation{
+		Authors: []string{"one", "two", "three"},
+		Topics:  []string{"go", "templates"},
+	}
+
+	tmpl := template.Must(template.New("presentation").Parse(tmplText))
+
+	tmpl = tmpl.Funcs(template.FuncMap{"join": strings.Join})                       // HL
+	tmpl = template.Must(tmpl.Parse(`{{define "list"}} {{join . " | "}} {{ end}}`)) // HL
+
+	err := tmpl.Execute(os.Stdout, p)
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/2016/state-of-go/time/time.go b/2016/state-of-go/time/time.go
new file mode 100644
index 0000000..a94d218
--- /dev/null
+++ b/2016/state-of-go/time/time.go
@@ -0,0 +1,18 @@
+// +build OMIT
+
+package main
+
+import (
+	"fmt"
+	"time"
+)
+
+func main() {
+	days := []string{"2015 Feb 29", "2016 Feb 29", "2017 Feb 29"}
+
+	fmt.Println("Are these days valid?")
+	for _, day := range days {
+		_, err := time.Parse("2006 Jan 2", day)
+		fmt.Printf("%v -> %v\n", day, err == nil)
+	}
+}