0xa things I love about Go

How learning Go made me a better programmer

This is not my idea

About me

How I came to Go

0xa things that I like about Go

Go

package main

import (
    "io"
    "log"
    "net/http"
    "os"
)

func main() {
    if len(os.Args) < 2 {
        log.Fatal("usage: fetch <url>")
    }
    resp, err := http.Get(os.Args[1])
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()
    io.Copy(os.Stdout, resp.Body)
}

Python

import sys
import urllib2

if __name__ == '__main__':
    if len(sys.argv) < 2:
        print >>sys.stderr, 'usage: fetch.py <url>'
        sys.exit(-1)
    r = urllib2.urlopen(sys.argv[1])
    print r.read()

Go Readability

Go fmt

go fmt <file.go>
(add-hook 'before-save-hook #'gofmt-before-save)
gofmt -r 'foo(a, b) -> foo(b, a)' foo.go

Python

pip install autoflake autopep8
pip install rope

Go Error handling

Go Error handling

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
)

func curl(url string) (string, error) {
    resp, err := http.Get(url)
    if err != nil {
        // local handling
        log.Printf("failed to fetch url %q: %v", url, err)
        // forward error to caller
        return "", err
    }
    defer resp.Body.Close()
    // ignore error
    data, _ := ioutil.ReadAll(resp.Body)
    return string(data), nil
}

func main() {
	data, err := curl("foo.com")
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}
	fmt.Println(data)
}

Python Error handling

def lazy(url):
    return urllib2.urlopen(url).read()

def cheating(url):
    try:
        return urllib2.urlopen(url).read()
    except:
        # local handling
        e = sys.exc_info()[0]
        print >>sys.stderr, 'failed to fetch url "%s": %s' % (url, e)
        # forward error
        raise

def careful(url):
    try:
        return urllib2.urlopen(url).read()
    except ValueError as e:
        # local handling
        print >>sys.stderr, 'failed to fetch url "%s": %s' % (url, e)
        raise
    except IOError as e:
        # ignore error
        pass

More about error handling

public void someMethod throws Exception {}
http.get(url, function(err, result) {
  //...
});
data Maybe t = Just t | Nothing
enum Result<T, U> { Ok(T), Err(U) }

Go Standard library

Python Stack

XML and JSON parsing in Go

XML parsing in Go

package main

import (
	"encoding/xml"
	"fmt"
	"log"
)

var feedXML = []byte(`<rss version="2.0">
  <channel>
    <item> <title>foo</title> <link>http://foo.com</link> </item>
  </channel>
</rss>`)

type Feed struct {
    Version string `xml:"version,attr"`
    Channel struct {
        Items []struct {
            Title string `xml:"title"`
            URL   string `xml:"link"`
        } `xml:"item"`
    } `xml:"channel"`
}

func main() {
    var feed Feed
    if err := xml.Unmarshal(feedXML, &feed); err != nil {
        log.Fatal("error parsing %q: %v", feedXML, err)
    }
    fmt.Println(feed.Version, feed.Channel.Items[0])
}

JSON parsing in Go

package main

import (
	"encoding/json"
	"fmt"
	"log"
)

var feedJSON = []byte(`{"data": {"children": [
   {"data": {"title": "foo", "url": "http://foo"}}
]}}`)

type Feed struct {
    Data struct {
        Children []struct {
            Data struct {
                Title string `json:"title"`
                URL   string `json:"url"`
            } `json:"data"`
        } `json:"children"`
    } `json:"data"`
}

func main() {
    var feed Feed
    if err := json.Unmarshal(feedJSON, &feed); err != nil {
        log.Fatal("error parsing %q: %v", feedJSON, err)
    }
    fmt.Println(feed.Data.Children[0].Data)
}

XML and JSON in Python

Go Packaging and Distribution

go get github.com/gorilla/rpc
import "github.com/gorilla/rpc"
go install

Packaging and Distribution

virtualenv myapp
. myapp/bin/activate
pip install Flask
python
>>> import flask
pom.xml
mvn install
package.json
npm install

Go tooling

go get
go fmt
go vet
go fix
go doc
go test [-memprofile=mem.out] [-cpuprofile=cpu.out] [-coverprofile=cover.out]
go <build|install|run> [-race]
gdb

Python tooling

virtualenv
pip
pyflakes
pep8
nose

Node.js tooling

npm
expresso
jslint

Go Concurrency

Go Concurrency

package main

import (
	"log"
	"net/http"
)

func main() {
	urls := []string{"https://news.ycombinator.com", "http://programming.reddit.com"}
    for _, url := range urls {
        resp, err := http.Get(url)
        if err != nil {
            log.Fatal(err)
        }
        log.Printf("%q: %s", resp.Status, url)
    }
}
package main

import (
	"log"
	"net/http"
)

func main() {
	urls := []string{"https://news.ycombinator.com", "http://programming.reddit.com"}
    ch := make(chan *http.Response)
    for _, u := range urls {
        go func(url string) {
            resp, err := http.Get(url)
            if err != nil {
                log.Fatal(err)
            }
            ch <- resp
        }(u)
    }
    for i := 0; i < len(urls); i++ {
        resp := <-ch
        log.Printf("%q: %s", resp.Status, resp.Request.URL)
    }
}

Node.js concurrency

request.get("https://news.ycombinator.com", function(err, response, body) {
  console.log(err, body);
  request.get("http://programming.reddit.com", function(err, response, body) {
    console.log(err, body);
  });
});
request.get("https://news.ycombinator.com", function(err, response, body) {
  console.log(err, body);
});
request.get("http://programming.reddit.com", function(err, response, body) {
  console.log(err, body);
});
async.map(urls, request.get, function(err, results) {
  console.log(err, results);
});
async.mapSeries(urls, request.get, function(err, results) {
  console.log(err, results);
});

Python concurrency

@tulip.task
def fetch(self, url):
    resp = yield from tulip.http.request('get', url)
    data = yield from resp.read()
    print data
    resp.close()

Other concurrency models

Go interfaces

Go interface

type Stringer interface {
    String() string
}

package main

import (
	"fmt"
)

// STRINGER OMIT
type Stringer interface {
	String() string
}

//STOP OMIT

type Foo struct {
}

func (f *Foo) String() string {
    return "foo"
}

type ID int

func (id ID) String() string {
    return fmt.Sprintf("#%08x", int(id))
}

func main() {
    ss := []Stringer{&Foo{}, ID(42)}
    fmt.Println(ss)
}

Python duck typing

class Foo(object):
    def __str__(self):
        return "foo"

class ID(object):
    def __init__(self, id):
        self.id = id

    def __str__(self):
        return "#%08x" % self.id

if __name__ == '__main__':
    print Foo(), ID(42)

Go interface composition

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

type ReadWriter interface {
    Reader
    Writer
}

Go composition

Go composition

package main

import (
	"fmt"
	"sync"
)

type Foo struct {
    sync.Mutex
}

func main() {
    foo := &Foo{}
    foo.Lock()
    foo.Unlock()
    safeDo(foo, func() {
        foo.Unsafe()
    })
}

type Locker interface {
    Lock()
    Unlock()
}

func safeDo(l Locker, f func()) {
    l.Lock()
    defer l.Unlock()
    f()
}


func (f *Foo) Unsafe() {
	fmt.Println("unsafe")
}

Go composition

package main

import (
	"io"
	"log"
	"net"
	"os"
)

type loggingConn struct {
    net.Conn // or *net.TCPConn
}

func (c *loggingConn) Read(b []byte) (int, error) {
    n, err := c.Conn.Read(b)
    log.Printf("read %d bytes: %q, err: %v", n, string(b[:n]), err)
    return n, err
}

func main() {
    conn, err := net.Dial("tcp", "towel.blinkenlights.nl:666")
    if err != nil {
        log.Fatal("failed to connect to bofh server:", err)
    }
    bofh := &loggingConn{conn}
    defer bofh.Close()
    io.Copy(os.Stdout, bofh)
}

Mixins

import SocketServer

class ThreadingTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    pass

Go community

Conclusion

More Go at OSCON

go nuts()

Thank you