go: it's not just for google

Post on 06-May-2015

6.838 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

DESCRIPTION

Another slab of Google Go madness with an emphasis on code and reflection.

TRANSCRIPT

Goit’s not just for Google

Eleanor McHugh

http://slides.games-with-brains.net/

Rough Cut!

compiled

garbage collected

imperative

package main

import “fmt”

const HELLO string = “hello”var WORLD string = “world”

func main() { fmt.Println(HELLO, WORLD)}

strongly typed

value

boolean, numeric, array

value

structure, interface

reference pointer, slice, string, map, channel

function function, method, closure

underlyingtype

methodset

expressedtype

underlyingtype

methodset

expressedtype

embeddedtypes

package Integer

type Int int

func (i *Int) Add(x int) { *i += Int(x)}

type Buffer []Int

func (b Buffer) Swap(i, j int) { b[i], b[j] = b[j], b[i]}

func (b Buffer) Clone() Buffer { s := make(Buffer, len(b)) copy(s, b) return s}

func (b Buffer) Move(i, n int) { if n > len(b) - i { n = len(b) - i } segment_to_move := b[:i].Clone() copy(b, b[i:i + n]) copy(b[n:i + n], segment_to_move)}

package main

import “fmt”import "Integer"

func main() { i := Integer.Buffer{0, 1, 2, 3, 4, 5} b := i.Clone() b.Swap(1, 2) b.Move(3, 2) b[0].Add(3) fmt.Printf(“b[0:2] = %v\n”, b[0:2])}

produces:b[0:2] = [6 4]

testable

func (b Buffer) Eq(o Buffer) (r bool) { if len(b) == len(o) { for i := len(b) - 1; i > 0; i-- { if b[i] != o[i] { return } } r = true } return}

func TestSwap(t *testing.T) { i := Buffer{0, 1, 2, 3, 4, 5} b := i.Clone() b.Swap(1, 2) if !b[1:3].Eq(Buffer{2, 1}) { t.Fatalf("b = %v", b) }}

package Vectorimport . "Integer"

type Vector struct { Buffer}

func (v *Vector) Clone() *Vector { return &Vector{v.Buffer.Clone()}}

func (v *Vector) Slice(i, j int) Buffer { return v.Buffer[i:j]}

package Vectorimport "testing"

func TestVectorSwap(t *testing.T) { i := Vector{Buffer{0, 1, 2, 3, 4, 5}} v := i.Clone() v.Swap(1, 2) r := Vector{Buffer{0, 2, 1, 3, 4, 5}} switch { case !v.Eq(r.Buffer): fallthrough case !v.Buffer.Eq(r.Buffer): t.Fatalf("b[0:5] = %v", v) }}

include $(GOROOT)/src/Make.inc

TARG=integer

GOFILES=\ integer.go\ vector.go

include $(GOROOT)/src/Make.pkg

func BenchmarkVectorClone6(b *testing.B) { v := Vector{Buffer{0, 1, 2, 3, 4, 5}} for i := 0; i < b.N; i++ { _ = v.Clone() }}

func BenchmarkVectorSwap(b *testing.B) { b.StopTimer() v := Vector{Buffer{0, 1, 2, 3, 4, 5}} b.StartTimer() for i := 0; i < b.N; i++ { v.Swap(1, 2) }}

$ gotest -bench="Benchmark"rm -f _test/scripts.a6g -o _gotest_.6 integer.go vector.go nominal_typing_test.go embedded_typing_benchmark_test.go embedded_typing_test.gorm -f _test/scripts.agopack grc _test/scripts.a _gotest_.6 PASSinteger.BenchmarkVectorSwap200000000 8 ns/opinteger.BenchmarkVectorClone6 10000000 300 ns/op

dynamic

type Adder interface { Add(j int) Subtract(j int) Result() interface{} Reset()}

type IAdder int

func (i IAdder) Add(j int) { i[0] += i[j]}

func (i IAdder) Subtract(j int) { i[0] -= i[j]}

func (i IAdder) Result() interface{} { return i[0]}

func (i IAdder) Reset() { i[0] = *new(int)}

type FAdder []float32

func (f FAdder) Add(j int) { f[0] += f[j]}

func (f FAdder) Subtract(j int) { f[0] -= f[j]}

func (f FAdder) Result() interface{} { return f[0]}

func TestAdder(t *testing.T) { var a Adder

a = IAdder{0, 1, 2} a.Add(1) if i.Result().(int) != 1 { t.Fatalf("IAdder::Add(1) %v != %v", a.Result(), 1) } a.Subtract(2) if a.Result().(int) != -1 { t.Fatalf("IAdder::Subtract(2) %v != %v", a.Result()), -1 }

a = FAdder{0.0, 1.0, 2.0} a.Add(1) if a.Result().(float32) != 1.0 { t.Fatalf("FAdder::Add(1) %v != %v", a.Result(), 1.0) }}

reflected

package generalise

import "reflect"

func Allocate(i interface{}, limit... int) (n interface{}) { switch v := reflect.ValueOf(i); v.Kind() { case reflect.Slice: l := v.Cap() if len(limit) > 0 { l = limit[0] } n = reflect.MakeSlice(v.Type(), l, l).Interface()

case reflect.Map: n = reflect.MakeMap(v.Type()).Interface() } return}

package generalise

import . "reflect"

func Allocate(i interface{}, limit... int) (n interface{}) { switch v := ValueOf(i); v.Kind() { case Slice: l := v.Cap() if len(limit) > 0 { l = limit[0] } n = MakeSlice(v.Type(), l, l).Interface()

case Map: n = MakeMap(v.Type()).Interface() } return}

func throwsPanic(f func()) (b bool) { defer func() { if x := recover(); x != nil { b = true } }() f() return}

func TestAllocate(t *testing.T) { var s2 []int

s1 := []int{0, 1, 2} m := map[int] int{1: 1, 2: 2, 3: 3} switch { case throwsPanic(func() { s2 = Allocate(s1, 1).([]int) }): t.Fatal("Unable to allocate new slice")

case len(s2) != 1 || cap(s2) != 1: t.Fatal("New slice should be %v not %v", make([]int, 0, 1), s2)

case throwsPanic(func() { Allocate(m) }): t.Fatal("Unable to allocate new map") }}

func Duplicate(i interface{}) (clone interface{}) { if clone = Allocate(i); clone != nil { switch clone := ValueOf(clone); clone.Kind() { case Slice: Copy(clone, ValueOf(i))

case Map: m := ValueOf(i) for _, k := range m.Keys() { clone.SetMapIndex(k, m.MapIndex(k)) } } } return}

func TestDuplicateSlice(t *testing.T) { s1 := []int{0, 1, 2} var s2 []int

if throwsPanic(func() { s2 = Duplicate(s1).([]int) }) { t.Fatalf("Unable to duplicate slice %v\n", s1) } switch { case len(s1) != len(s2): fallthrough case cap(s1) != cap(s2): fallthrough case s1[0] != s2[0]: fallthrough case s1[1] != s2[1]: fallthrough case s1[2] != s2[2]: fallthrough t.Fatalf("Duplicating %v produced %v", s1, s2) }}

func TestDuplicateMap(t *testing.T) { m1 := map[int]int{1: 1, 2: 2, 3: 3} var m2 map[int]int

if throwsPanic(func() { m2 = Duplicate(m1).(map[int]int) }) { t.Fatalf("Unable to duplicate map %v\n", m1) }

switch { case len(m1) != len(m2): fallthrough case m1[1] != m2[1]: fallthrough case m1[2] != m2[2]: fallthrough case m1[3] != m2[3]: fallthrough t.Fatalf("Duplicating %v produced %v", m1, m2) }}

low-level

package raw

import . "reflect"import "unsafe"

var _BYTE_SLICE Type = Typeof([]byte(nil))

type MemoryBlock interface { ByteSlice() []byte}

func valueHeader(v reflect.Value) (Header *reflect.SliceHeader) { if v.IsValid() { s := int(v.Type().Size()) header = &reflect.SliceHeader{ v.UnsafeAddr(), s, s } } return}

func SliceHeader(i interface{}) (Header *SliceHeader, Size, Align int) { switch value := Indirect(ValueOf(i)); value.Kind() { case Slice: Header = (*SliceHeader)(unsafe.Pointer(value.UnsafeAddr())) t := value.Type().Elem() Size = int(t.Size()) Align = t.Align() case Interface: Header, Size, Align = SliceHeader(value.Elem()) } return}

func Scale(oldHeader *SliceHeader, oldESize, newESize int) (h *SliceHeader) { if oldHeader != nil { s := float64(oldESize) / float64(newESize) h = &SliceHeader{ Data: oldHeader.Data } h.Len = int(float64(oldHeader.Len) * s) h.Cap = int(float64(oldHeader.Cap) * s) } return}

func ByteSlice(i interface{}) []byte { switch i := i.(type) { case []byte: return i case MemoryBlock: return i.ByteSlice() }

var header *SliceHeader switch v := ValueOf(i); value.Kind() { case Interface, Ptr: header = valueHeader(v.Elem())

case Slice: h, s, _ := SliceHeader(i) header = Scale(h, s, 1)

case String: s := v.Get() h := *(*StringHeader)(unsafe.Pointer(&s)) header = &SliceHeader{ h.Data, h.Len, h.Len }

default: header = valueHeader(v) } return unsafe.Unreflect(_BYTE_SLICE, unsafe.Pointer(header)).([]byte)}

concurrent

package mainimport "fmt"

func main() { var c chan int c = make(chan int) limit := 16 go func() { for i := limit; i > 0; i-- { fmt.Print(<-c) } }() for i := limit; i > 0; i-- { select { case c <- 0: case c <- 1: } }}

produces:0110011101011010

func main() { var c chan int c = make(chan int, 16) go func() { for i := 16; i > 0; i-- { fmt.Print(<-c) } }() go func() { select { case c <- 0: case c <- 1: } }() for {}}

produces:0110011101011010

package generalise

type SignalSource func(status chan bool)

func Wait(s SignalSource) { done := make(chan bool) defer close(done) go s(done) <-done}

func WaitAll(count int, s SignalSource) { done := make(chan bool) defer close(done) go s(done) for i := 0; i < count; i++ { <- done }}

type Iteration func(k, x interface{}) bool

func (i Iteration) apply(k, v interface{}, c chan bool) { go func() { c <-i(k, v) }()}

func (f Iteration) Each(c interface{}) { switch c := ValueOf(c); c.Kind() { case Slice: WaitAll(c.Len(), SignalSource(func(done chan bool) { for i := 0; i < c.Len(); i++ { f.apply(i, c.Elem(i).Interface(), done) }}))

case Map: WaitAll(c.Len(), SignalSource(func(done chan bool) { for _, k := range c.Keys() { f.apply(k, c.Elem(k).Interface(), done) }})) }}

type Results chan interface{}

type Combination func(x, y interface{}) interface{}

func (f Combination) Reduce(c, s interface{}) (r Results) { r = make(Results) go func() { Iteration(func(k, x interface{}) (ok bool) { s = f(s, x) return true }).Each(c) r <- s }() return}

type Transformation func(x interface{}) interface{}

func (t Transformation) Transform(x interface{}) Value { return ValueOf(t(x))}

func (t Transformation) Map(c interface{}) (r interface{}) { r = Allocate(c) if i := MapIterator(r); i != nil { i.Each(c) } return}

func MapIterator(c interface{}) (i Iteration) { switch n := ValueOf(c); n.Kind() { case Slice: i = Iteration(func(k, x interface{}) bool { n.Elem(k.(int)).SetValue(t.GetValue(x)) return true })

case Map: i = Iteration(func(k, x interface{}) bool { n.SetMapIndex(ValueOf(k), t.GetValue(x)) return true }) } return}

func main() { s := []int{0, 1, 2, 3, 4, 5} d := Transformation(func(x interface{}) interface{} { return x.(int) * 2 } ).Map(s) sum := Combination(func(x, y interface{}) interface{} { return x.(int) + y.(int) }) fmt.Printf("s = %v, sum = %v\n", s, (<- sum.Reduce(s, 0)).(int)) fmt.Printf("d = %v, sum = %v\n", d, (<- sum.Reduce(d, 0)).(int))}

produces:s = [0 1 2 3 4 5], sum = 15d = [0 2 4 6 8 10], sum = 30

extensible

include $(GOROOT)/src/Make.inc

TARG=sqlite3

CGOFILES=\ sqlite3.go\ database.go

ifeq ($(GOOS),darwin)CGO_LDFLAGS=/usr/lib/libsqlite3.0.dylibelseCGO_LDFLAGS=-lsqlite3endif

include $(GOROOT)/src/Make.pkg

package sqlite3

// #include <sqlite3.h>import "C"import "fmt"import "os"

type Database struct { handle *C.sqlite3 Filename string Flags C.int}

func (db *Database) Error() os.Error { return Errno(C.sqlite3_errcode(db.handle))}

const( OK = Errno(iota) ERROR CANTOPEN = Errno(14))

var errText = map[Errno]string { ERROR: "SQL error or missing database", CANTOPEN: "Unable to open the database file",}

type Errno int

func (e Errno) String() (err string) { if err = errText[e]; err == "" { err = fmt.Sprintf("errno %v", int(e)) } return }

func (db *Database) Open(flags... int) (e os.Error) { db.Flags = 0 for _, v := range flags { db.Flags = db.Flags | C.int(v) } f := C.CString(db.Filename) if err := Errno(C.sqlite3_open_v2(f, &db.handle, db.Flags, nil)); err != OK { e = err } else if db.handle == nil { e = CANTOPEN } return}

func (db *Database) Close() { C.sqlite3_close(db.handle) db.handle = nil}

func Open(filename string, flags... int) (db *Database, e os.Error) { defer func() { if x := recover(); x != nil { db.Close() db = nil e = ERROR } }() db = &Database{ Filename: filename } if len(flags) == 0 { e = db.Open( C.SQLITE_OPEN_FULLMUTEX, C.SQLITE_OPEN_READWRITE, C.SQLITE_OPEN_CREATE ) } else { e = db.Open(flags...) } return}

fun!

finding out more

http://golang.org/

twitter://#golightly

http://github.com/feyeleanor/

wikipedia or google

top related