Go for Pythonistas Francesc Campoy Flores Gopher at Google

Video A recording of this talk is available. youtu.be/elu0VpLzJL8 2

Goal of this talk Whetting your appetite for Go 3

My tactics 1. Showing you how Go is like Python. 2. Showing you how Go is not like Python. 4

Python, Go, and me Software Engineer at Google: Feb 11-Aug 12 Lots of C++ and Python. SQL to C++ compiler in Python. Go Developer Relations: Aug 12 - datetime.now() Lots of Go. 5

Things I don't like about Python (it'll be short) 6

Beautiful and simple Dynamic typing - nice because it's concise, like Python. a = "hello" b = 1 # but also a = 2 Static typing - can be verbose, like Java or C++. Foo foo = new Foo(); Static typing with inferred types, like Go. a := "hello" b := 1 // but no a = 2 Statically-typed Python? Check mypy and Cython. 7

Run time pyrotechnics #!/usr/bin/python import random name = 'pythonista' # This code only works half of the time. if random.random() > 0.5: print 'hey '+name+', you win!' else: print 'sorry '+nane+', you lose' I don't want start a flame war here but ... 100% code coverage is a symptom Code coverage should point you to untested cases.

Not a way to find typos!

100% code coverage doesn't mean bug free. 8

Other things I don't like Deploying - managing dependencies. Performance - "not too slow" is often not fast enough. Magic! (e.g.: __magic__ : **kargs , __getattr__ ) A list of magic methods in Python: www.rafekettler.com/magicmethods.html 9

And I *do* like concurrency! A lot has been said about Python's infamous Global Interpreter Lock. You should watch Mindblowing Python GIL, by David Beazley. 10

Things I like about Python 11

Things I like about Python The Zen of Python. (Go and the Zen of Python) Hashes and arrays are part of the language. The standard library. Magic! A bit of code can do a lot. 12

A bit of code 13

fib.py Have you ever heard of Fibonacci? #!/usr/bin/python def fib(n): a, b = 0, 1 for i in range(n): a, b = b, a + b return b def fib_rec(n): if n <= 1: return 1 else: return fib_rec(n-1) + fib_rec(n-2) for x in range(10): print fib(x), fib_rec(x) 14

fib.go Something familiar? // +build ignore,OMIT package main import "fmt" func fib(n int) int { a, b := 0, 1 for i := 0; i < n; i++ { a, b = b, a+b } return b } func fibRec(n int) int { if n <= 1 { return 1 } return fibRec(n-1) + fibRec(n-2) } func main() { for i := 0; i < 10; i++ { fmt.Println(fib(i), fibRec(i)) } } 15

Fibonacci without generators? What? Python generators are awesome. def fib(n): a, b = 0, 1 for i in range(n): a, b = b, a + b yield a Mechanically complex. #!/usr/bin/python def fib(n): a, b = 0, 1 for i in range(n): a, b = b, a + b yield a f = fib(10) try: while True: print f.next() except StopIteration: print 'done' for x in fib(10): print x print 'done' But very easy to use. #!/usr/bin/python def fib(n): a, b = 0, 1 for i in range(n): a, b = b, a + b yield a f = fib(10) try: while True: print f.next() except StopIteration: print 'done' for x in fib(10): print x print 'done' 16

Python generators Note the generator executes concurrently. Hmm... I like concurrency. 17

Go concurrency Based on goroutines and channels. Goroutines: very light processing actors (the gophers). Channels: typed, synchronized, thread-safe pipes (the arrows). 18

"Generator" goroutines 19

"Generator" goroutines Uses a channel send instead of yield . func fib(c chan int, n int) { a, b := 0, 1 for i := 0; i < n; i++ { a, b = b, a+b c <- a } close(c) } // +build ignore,OMIT package main import "fmt" func fib(c chan int, n int) { a, b := 0, 1 for i := 0; i < n; i++ { a, b = b, a+b c <- a // HL } close(c) } func main() { c := make(chan int) go fib(c, 10) for x := range c { fmt.Println(x) } } 20

"Generator" goroutines A more generator-like style: // +build ignore,OMIT package main import "fmt" func fib(n int) chan int { c := make(chan int) go func() { a, b := 0, 1 for i := 0; i < n; i++ { a, b = b, a+b c <- a } close(c) }() return c } func main() { for x := range fib(10) { fmt.Println(x) } } 21

Exercise: generating prime numbers Write a function that returns a channel and sends the first n prime numbers on

it. Given the function prime : // prime returns true if n is a prime number. func prime(n int) bool { for i := 2; i < n; i++ { if n%i == 0 { return false } } return true } Use the Go playground: golang.org/s/go4py-ex1 22

Solution: generating prime numbers func primes(n int) chan int { c := make(chan int) go func() { for i := 1; n > 0; i++ { if prime(i) { c <- i n-- } } close(c) }() return c } // +build ignore,OMIT package main import "fmt" // prime returns true if n is a prime number. func prime(n int) bool { for i := 2; i < n; i++ { if n%i == 0 { return false } } return true } // primes returns a channel of ints on which it writes the first n prime // numbers before closing it. func primes(n int) chan int { c := make(chan int) go func() { for i := 1; n > 0; i++ { if prime(i) { c <- i n-- } } close(c) }() return c } func main() { for p := range primes(10) { fmt.Println(p) } } 23

Exercise: Fibonacci primes Write a filterPrimes function that takes a channel of ints as a

parameter and returns another channel of ints. All the prime numbers that filterPrimes receives from the input channel are

sent into the output channel. Complete this code snippet: golang.org/s/go4py-ex2 24

Solution: Fibonacci primes func filterPrimes(cin chan int) chan int { cout := make(chan int) go func() { for v := range cin { if prime(v) { cout <- v } } close(cout) }() return cout } // +build ignore,OMIT package main import "fmt" // prime returns true if n is a prime number. func prime(n int) bool { for i := 2; i < n; i++ { if n%i == 0 { return false } } return true } // fib returns a channel on which the first n Fibonacci numbers are written. func fib(n int) chan int { c := make(chan int) go func() { a, b := 0, 1 for i := 0; i < n; i++ { a, b = b, a+b c <- a } close(c) }() return c } // filterPrimes returns a channel of ints on which it writes all the prime // numbers read from cin, and closes the returned channel when cin is closed. func filterPrimes(cin chan int) chan int { cout := make(chan int) go func() { for v := range cin { if prime(v) { cout <- v } } close(cout) }() return cout } func main() { for p := range filterPrimes(fib(20)) { fmt.Println(p) } } 25

But there's much more Goroutines and channels aren't just for generators. They can be used to model

all kinds of concurrent systems. To learn more: Concurrency patterns, by Rob Pike Advanced Concurrency Patterns, by Sameer Ajmani 26

Object-oriented Go 27

Object-oriented Go A type declaration. type Name struct { First string Middle string Last string } A method declaration. func (n Name) String() string { return fmt.Sprintf("%s %c. %s", n.First, n.Middle[0], strings.ToUpper(n.Last)) } Constructing a Name and using it. // +build ignore,OMIT package main import ( "fmt" "strings" ) type Name struct { First string Middle string Last string } func (n Name) String() string { return fmt.Sprintf("%s %c. %s", n.First, n.Middle[0], strings.ToUpper(n.Last)) } type SimpleName string func (s SimpleName) String() string { return string(s) } func main() { n := Name{"William", "Mike", "Smith"} fmt.Printf("%s", n.String()) return // second OMIT n = Name{"William", "Mike", "Smith"} fmt.Println(n) } 28

Methods on anything There's more to types than structs. type SimpleName string You can define methods on any type. func (s SimpleName) String() string { return string(s) } Or almost any type. func (s string) NoWay() You can only define methods on types within the same package. 29

Duck typing 30

Duck typing If it walks like a duck ... What defines a duck? Is there an explicit list of "duck" features? What if the duck is not exactly a duck? s/duck/file-like object/g 31

Quack? 32

Go interfaces Simply a set of methods. From the fmt package: type Stringer interface { String() string } fmt.Println calls the String method if the parameter is a Stringer . // +build ignore,OMIT package main import ( "fmt" "strings" ) type Name struct { First string Middle string Last string } func (n Name) String() string { return fmt.Sprintf("%s %c. %s", n.First, n.Middle[0], strings.ToUpper(n.Last)) } type SimpleName string func (s SimpleName) String() string { return string(s) } func main() { n := Name{"William", "Mike", "Smith"} fmt.Printf("%s", n.String()) return n = Name{"William", "Mike", "Smith"} fmt.Println(n) } A type with all the methods of the interface implements the interface. Implicit satisfaction == No "implements" Structural typing: it doesn't just sound like a duck, it is a duck. And that's checked at compile time. 33

Decorators 34

Decorators A convenient way to wrap a function. def auth_required(myfunc): def checkuser(self): user = parse_qs(urlparse(self.path).query).get('user') if user: self.user = user[0] myfunc(self) else: self.wfile.write('unknown user') return checkuser A function can be decorated using @ . class myHandler(BaseHTTPRequestHandler): @auth_required def do_GET(self): self.wfile.write('Hello, %s!' % self.user) 35

Decorators If we run it. #!/usr/bin/python from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer from urlparse import urlparse,parse_qs PORT_NUMBER = 8080 def auth_required(myfunc): def checkuser(self): user = parse_qs(urlparse(self.path).query).get('user') if user: self.user = user[0] myfunc(self) else: self.wfile.write('unknown user') return checkuser class myHandler(BaseHTTPRequestHandler): @auth_required def do_GET(self): self.wfile.write('Hello, %s!' % self.user) try: server = HTTPServer(('', PORT_NUMBER), myHandler) server.serve_forever() except KeyboardInterrupt: server.socket.close() This is unauthorized: localhost:8080/hi This is authorized: localhost:8080/hi?user=john 36

Decorators in Go? Not exactly, but close enough. Go doesn't provide decorators in the language, but its function literal syntax and simple scoping rules make it easy to do something similar. var hiHandler = authRequired( func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hi, %v", r.FormValue("user")) }, ) A wrapper function. func authRequired(f http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.FormValue("user") == "" { http.Error(w, "unknown user", http.StatusForbidden) return } f(w, r) } } 37

Decorators in Go? // +build ignore,OMIT package main import ( "fmt" "net/http" ) func authRequired(f http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.FormValue("user") == "" { http.Error(w, "unknown user", http.StatusForbidden) return } f(w, r) } } var hiHandler = authRequired( func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hi, %v", r.FormValue("user")) }, ) func main() { http.HandleFunc("/hi", hiHandler) http.ListenAndServe(":8080", nil) } This is unauthorized: localhost:8080/hi This is authorized: localhost:8080/hi?user=john 38

Exercise: errors in HTTP handlers In Go, functions can return errors to indicate that something bad happened. The net/http package from the standard library defines the type HandlerFunc . type HandlerFunc func(ResponseWriter, *Request) But it's often useful to unify the error handling into a single function to avoid

repetition. type errorHandler func(http.ResponseWriter, *http.Request) error Write a decorator that given a errorHandler returns a http.HandlerFunc .

If an error occurs it logs it and returns an http error page. 39

Exercise: errors in HTTP handlers (continuation) Given the function handler . func handler(w http.ResponseWriter, r *http.Request) error { name := r.FormValue("name") if name == "" { return fmt.Errorf("empty name") } fmt.Fprintln(w, "Hi,", name) return nil } We want to use it as follows. http.HandleFunc("/hi", handleError(handler)) Implement handleError using the playground. golang.org/s/go4py-ex3 40

Solution: errors in HTTP handlers func handleError(f errorHandler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { err := f(w, r) if err != nil { log.Printf("%v", err) http.Error(w, "Oops!", http.StatusInternalServerError) } } } // Fake request without 'name' parameter. r := &http.Request{} w := newDummyResp() handleError(handler)(w, r) fmt.Println("resp a:", w) // +build ignore,OMIT package main import ( "bytes" "fmt" "io" "log" "net/http" ) type errorHandler func(http.ResponseWriter, *http.Request) error func handleError(f errorHandler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { err := f(w, r) if err != nil { log.Printf("%v", err) http.Error(w, "Oops!", http.StatusInternalServerError) } } } func handler(w http.ResponseWriter, r *http.Request) error { name := r.FormValue("name") if name == "" { return fmt.Errorf("empty name") } fmt.Fprintln(w, "Hi,", name) return nil } // resp implements http.ResponseWriter writing type dummyResp struct { io.Writer h int } func newDummyResp() http.ResponseWriter { return &dummyResp{Writer: &bytes.Buffer{}} } func (w *dummyResp) Header() http.Header { return make(http.Header) } func (w *dummyResp) WriteHeader(h int) { w.h = h } func (w *dummyResp) String() string { return fmt.Sprintf("[%v] %q", w.h, w.Writer) } func main() { http.HandleFunc("/hi", handleError(handler)) // ListenAndServe is not allowed on the playground. // http.ListenAndServe(":8080", nil) // In the playground we call the handler manually with dummy requests. // Fake request without 'name' parameter. r := &http.Request{} w := newDummyResp() handleError(handler)(w, r) fmt.Println("resp a:", w) // Fake request with 'name' parameter 'john'. r.Form["name"] = []string{"john"} w = newDummyResp() handleError(handler)(w, r) fmt.Println("resp b:", w) } 41

Monkey patching 42

Monkey patching "A monkey patch is a way to extend or modify the run-time code of dynamic languages without altering the original source code." - Wikipedia 43

Monkey patching Also known as "duck punching" ... poor duck. Often used for testing purposes. For example, say we want to test this function: def say_hi(usr): if auth(usr): print 'Hi, %s' % usr else: print 'unknown user %s' % usr Which depends on a function that makes an HTTP request: def auth(usr): try: r = urllib.urlopen(auth_url + '/' + usr) return r.getcode() == 200 except: return False 44

Monkey patching We can test say_hi without making HTTP requests by stubbing out auth : #!/usr/bin/python import urllib auth_url = 'http://google.com' def auth(usr): try: r = urllib.urlopen(auth_url + '/' + usr) return r.getcode() == 200 except: return False def say_hi(usr): if auth(usr): print 'Hi, %s' % usr else: print 'unknown user %s' % usr def sayhitest(): # Test authenticated user globals()['auth'] = lambda x: True say_hi('John') # Test unauthenticated user globals()['auth'] = lambda x: False say_hi('John') sayhitest() 45

Gopher punching! The same effect can be achieved in Go. func sayHi(user string) { if !auth(user) { fmt.Printf("unknown user %v

", user) return } fmt.Printf("Hi, %v

", user) } Which depends on var auth = func(user string) bool { res, err := http.Get(authURL + "/" + user) return err == nil && res.StatusCode == http.StatusOK } 46

Gopher punching! Our test code can change the value of auth easily. // +build ignore,OMIT package main import ( "fmt" "net/http" ) var authURL = "" var auth = func(user string) bool { res, err := http.Get(authURL + "/" + user) return err == nil && res.StatusCode == http.StatusOK } func sayHi(user string) { if !auth(user) { fmt.Printf("unknown user %v

", user) return } fmt.Printf("Hi, %v

", user) } func TestSayHi() { auth = func(string) bool { return true } sayHi("John") auth = func(string) bool { return false } sayHi("John") } func init() { auth = func(string) bool { return true } } func TestAnythingElse() { // auth has been already set to the fake version } func main() { TestSayHi() } 47

Conclusion 48

Conclusion Go is a bit like Python simple

flexible

fun but a bit different too fast

concurrent

statically typed Disclaimer : "No pythons, ducks, monkeys or gophers were harmed while writing this talk" 49

Try it Next steps golang.org Learn Go from your browser tour.golang.org The community: golang-nuts groups.google.com/d/forum/golang-nuts 50