The Path to Go 1.1

language, stdlib, tools, and performance

28 March 2013

Jesse Szwedko

Software Engineer, ModCloth

Sheena McCoy

Software Engineer, ModCloth

The Path to Go 1.1

Language Changes

Method Value

Functions that have been bound to a specific receiver value

func (w io.Writer) Write(p []byte) (n int, err error)
package main

import (
	"io"
	"os"
)

func main() {
    var w = os.Stdout
    var f1 func(p []byte) (n int, err error)
    var f2 func(w io.Writer, p []byte) (n int, err error)

    //Method value (new)
    f1 = w.Write
    f1([]byte("hello ")) //writes the byte array to w

    //Method expression (pre-existing)
    f2 = (io.Writer).Write
    f2(w, []byte("world")) //writes the byte array to w

    w.Write([]byte("!\n"))
}

Useful for passing struct methods to APIs requiring a callback.
E.g. http.ServeMux.ServeHTTP

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))

Previously this would require wrapping in an anonymous function.

Method Value

package main

import (
	"fmt"
	"strings"
)

type RotN struct {
    Rotation rune
}

func (me *RotN) rot(r rune) rune {
    switch {
    case r >= 'A' && r <= 'Z':
        return 'A' + (r-'A'+me.Rotation)%26
    case r >= 'a' && r <= 'z':
        return 'a' + (r-'a'+me.Rotation)%26
    }
    me.Rotation += 1
    return r
}

func main() {
    foo := &RotN{Rotation: 1}
    fmt.Println(strings.Map(foo.rot, "'Twas brillig and the slithy gopher..."))
}

Return Requirements

Functions ending in terminating statements no longer require a panic() or return

package main

import ()

func forever() {
    for {
        //do stuff
    }
    return
}

func branching(x int) bool {
    if x > 10 {
        return true
    } else {
        return false
    }
    panic("This should not be happening")
}

func main() {
}

You can use `go vet` to find unreachable code

Other Language Changes

Integer division by zero

package main

func main() {
    x := 5 / (1-1)
}

Surrogate halves in Unicode (UTF-16) literals

package main

import (
  "fmt"
  "unicode/utf8"
)

func main() {
    fmt.Printf("%+q\n", utf8.RuneError)
    fmt.Printf("%+q\n", string(0xD800)) //Prints utf8.RuneError
}

Standard Library Changes

reflect.MakeFunc

Allows for implementation of pseudo-generic functions with type safety with respect to the user application.

func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value

Execution of generated function

reflect.MakeFunc

package main

import (
	"fmt"
	"reflect"
)

func swap(in []reflect.Value) []reflect.Value {
    return []reflect.Value{in[1], in[0]}
}

func makeSwap(fptr interface{}) {
    fn := reflect.ValueOf(fptr).Elem()

    v := reflect.MakeFunc(fn.Type(), swap)

    fn.Set(v)
}

func main() {
    var intSwap func(int, int) (int, int)
    makeSwap(&intSwap)
    fmt.Println(intSwap(0, 1))

    var floatSwap func(float64, float64) (float64, float64)
    makeSwap(&floatSwap)
    fmt.Println(floatSwap(2.72, 3.14))
}

reflect.Select

Allows for dymanic generation of select statements

const (
        _               SelectDir = iota
        SelectSend              // case Chan <- Send
        SelectRecv              // case <-Chan:
        SelectDefault           // default
)

type SelectCase struct {
    Dir  SelectDir // direction of case
    Chan Value     // channel to use (for send or receive)
    Send Value     // value to send (for send)
}

func Select(cases []SelectCase) (chosen int, recv Value, recvOK bool)

Muxing Select Go 1

package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

type Output struct {
	Index   int
	Payload int
}

func receive(inputs []chan int) <-chan *Output {
    var wg sync.WaitGroup
    output := make(chan *Output)

    for i, input := range inputs {
        wg.Add(1)
        go func(index int, input chan int) {
        L:
            for {
                select {
                case payload, ok := <-input:
                    if !ok {
                        wg.Done()
                        break L
                    }
                    output <- &Output{Index: index, Payload: payload}
                }
            }
        }(i, input)
    }
    go func(numChan int) {
        defer close(output)
        wg.Wait()
    }(len(inputs))

	return output
}

func getChannels() []chan int {
	inputs := make([]chan int, 10)
	for i := 0; i < len(inputs); i++ {
		input := make(chan int)
		go func() {
			defer close(input)
			for j := 0; j < 10; j++ {
				input <- j
				time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
			}
		}()
		inputs[i] = input
	}
	return inputs
}

func printOutput(output <-chan *Output) {
L:
	for {
		select {
		case rs, ok := <-output:
			if !ok {
				break L
			}
			fmt.Printf("Received from %d: %d\n", rs.Index, rs.Payload)
		}
	}
}

func main() {
	rand.Seed(time.Now().Unix())
	printOutput(receive(getChannels()))
}

Muxing Select Go 1.1 (reflect.Select)

package main

import (
	"fmt"
	"math/rand"
	"reflect"
	"time"
)

type Output struct {
	Index   int
	Payload int
}

func receive(inputs []chan int) <-chan *Output {
    cases := make([]reflect.SelectCase, len(inputs))
    output := make(chan *Output)

    for i, input := range inputs {
        cases[i] = reflect.SelectCase{Dir: reflect.SelectRecv, Chan: reflect.ValueOf(input)}
    }

    go func() {
        defer close(output)
        for {
            index, recv, ok := reflect.Select(cases)
            if !ok {
                cases[index] = cases[len(cases)-1]
                cases = cases[0 : len(cases)-1]

                if len(cases) == 0 {
                    break
                }
                continue
            }

            output <- &Output{Index: index, Payload: int(recv.Int())}
        }
    }()

	return output
}

func getChannels() []chan int {
	inputs := make([]chan int, 10)
	for i := 0; i < len(inputs); i++ {
		input := make(chan int)
		go func() {
			defer close(input)
			for j := 0; j < 10; j++ {
				input <- j
				time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
			}
		}()
		inputs[i] = input
	}
	return inputs
}

func printOutput(output <-chan *Output) {
L:
	for {
		select {
		case rs, ok := <-output:
			if !ok {
				break L
			}
			fmt.Printf("Received from %d: %d\n", rs.Index, rs.Payload)
		}
	}
}

func main() {
	printOutput(receive(getChannels()))
}

Muxing in Newsqueak

a: array[N] of chan of int = mk();
i, j: int;
select {
    case <-a[]:         print("any");
    case i = <-a[]:     print("A", i);
    case <-a[i=]:       print("B", i);
    case i = <-a[j=]:   print("C", i, j);
}

Maybe someday.

CSP in Newsqueak

Tool Chain Changes

go build

go get

go test

go fix

Backwards "Incompatible" Changes

Performance

Typical improvements relative to Go 1.0 seem to be about 30%-40%

Current State of 1.1 Release

Thank you

Jesse Szwedko

Software Engineer, ModCloth

Sheena McCoy

Software Engineer, ModCloth

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.)