Ro u gh Cut! Go it s not just for Google Eleanor McHugh http://slides.games-with-brains.net/
compiled
garbage collected
imperative
package main import fmt const HELLO string = hello var WORLD string = world func main() { fmt.println(hello, WORLD)
strongly typed
boolean, numeric, array value structure, interface reference pointer, slice, string, map, channel function function, method, closure
underlying type method set expressed type
underlying type method set expressed type embedded types
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 Vector import. "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 Vector import "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.a 6g -o _gotest_.6 integer.go vector.go nominal_typing_test.go embedded_typing_benchmark_test.go embedded_typing_test.go rm -f _test/scripts.a gopack grc _test/scripts.a _gotest_.6 PASS integer.benchmarkvectorswap 200000000 8 ns/op integer.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: return n = reflect.makemap(v.type()).interface()
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: return n = MakeMap(v.Type()).Interface()
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: return m := ValueOf(i) for _, k := range m.keys() { clone.setmapindex(k, m.mapindex(k))
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 main import "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: return i = Iteration(func(k, x interface{) bool { n.setmapindex(valueof(k), t.getvalue(x)) return true )
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 = 15 d = [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.dylib else CGO_LDFLAGS=-lsqlite3 endif include $(GOROOT)/src/Make.pkg
package sqlite3 // #include <sqlite3.h> import "C" import "fmt" import "os" type Database struct { handle Filename Flags *C.sqlite3 string 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