Expanded and updated version of my Strange Loop 2010 presentation on how to program in Google's Go language with an emphasis on the kind of tricks you might want to perform when implementing a language runtime.
2. portrait of an artist...
physics major
http:/ feyele
embedded controllers
/
software reliability
dynamic languages
network scaling or.tel an
questionable taste in music Elean or McHugh
8. Elric sent his mind into twisting tunnels of logic,
across endless plains of ideas, through mountains
of symbolism and endless universes of alternate
truths; he sent his mind out further and further
and as it went he sent with it the words which
issued from his writhing lips -- words that few
of his contemporaries would understand...
- Elric of Melniboné, Michael Moorcock
15. agnostic
no blessed programming languages
flexible platform abstractions
write once, run everywhere it matters
16. heterogeneous
a system comprises many components
components may differ in purpose and design
but they cooperate to solve problems
17. virtualisation
design discrete Turing machines
implement these machines in software
compile programs to run on them
18. networks
machines cooperate by sending messages
machine states can be serialised as messages
messages transcend process and host boundaries
19. caveat lector
danger! we’re entering strange territory
our map is missing major landmarks
and will be riddled with inaccuracies
so please tread carefully
try not to disturb the local wildlife
and don’t be put off by the pages of code
21. an elegant language
a statically-typed compiled language
object-oriented
static type declaration
dynamic type inference
garbage collection
concurrency via communication (CSP)
48. package adder
import "testing"
func TestFAdder(t *testing.T) {
error := "Result %v != %v"
f := FAdder{0.0, 1.0, 2.0}
f.Add(1)
if f.Result().(float32) != 1.0 { t.Fatalf(error, f.Result(), 1.0) }
f.Subtract(2)
if i.Result().(float32) != -1.0 { t.Fatalf(error, i.Result()), -1.0 }
var r Calculator = FAdder{-1.0, 1.0, 2.0}
for n, v := range r.(FAdder) {
if f[n] != v { t.Fatalf("Adder %v should be %v", f, r) }
}
r.Reset()
if r.Result().(float32) != *new(float32) {
t.Fatalf(error, r.Result(), *new(float32))
}
}
49. package adder
import "testing"
func TestAddingMachine(t *testing.T) {
error := "Result %v != %v"
a := &AddingMachine{ Adder: FAdder{0.0, 1.0, 2.0} }
a.Add(1)
if f, ok := a.Result().(float32); !ok {
t.Fatal("Result should be a float32")
} else if f != 1.0 {
t.Fatalf(error, a.Result(), 1.0)
}
a.Subtract(2)
if a.Result().(float32) != -1.0 { t.Fatalf(error, a.Result(), -1.0) }
r := FAdder{-1.0, 1.0, 2.0}
for n, v := range a.Adder.(FAdder) {
if r[n] != v { t.Fatalf("Adder %v should be %v", a, r) }
}
}
50. package generalise
import "reflect"
func Allocate(i interface{}, limit... int) (n interface{}) {
v := reflect.NewValue(i)
switch v := v.(type) {
case *reflect.SliceValue:
l := v.Cap()
if len(limit) > 0 { l = limit[0] }
t := v.Type().(*reflect.SliceType)
n = reflect.MakeSlice(t, l, l).Interface()
case *reflect.MapValue:
t := v.Type().(*reflect.MapType)
n = reflect.MakeMap(t).Interface()
}
return
}
51. package generalise
import . "reflect"
func SwapSlices(i interface{}, d, s, n int) {
if v, ok := NewValue(i).(*SliceValue); ok {
source := v.Slice(s, s + n)
destination := v.Slice(d, d + n)
temp := NewValue(Allocate(i, n)).(*SliceValue)
Copy(temp, destination)
Copy(destination, source)
Copy(source, temp)
} else {
panic(i)
}
}
52. package generalise
import . "reflect"
func Duplicate(i interface{}) (clone interface{}) {
if clone = Allocate(i); clone != nil {
switch clone := NewValue(clone).(type) {
case *SliceValue:
s := NewValue(i).(*SliceValue)
Copy(clone, s)
case *MapValue:
m := NewValue(i).(*MapValue)
for _, k := range m.Keys() {
clone.SetElem(k, m.Elem(k))
}
}
}
return
}
65. package main
import "fmt"
func main() {
var c chan int
c = make(chan int)
go func() {
for {
fmt.Print(<-c)
}
}()
for {
select {
case c <- 0:
case c <- 1:
}
}
} produces:
01100111010110...
66. package main
import "fmt"
func main() {
var c chan int
c = make(chan int, 16)
go func() {
for {
fmt.Print(<-c)
}
}()
go func() {
select {
case c <- 0:
case c <- 1:
}
}()
for {} produces:
} 01100111010110...
67. package generalise
import . "reflect"
type SignalSource func(status chan bool)
func (s SignalSource) Wait() {
done := make(chan bool)
defer close(done)
go s(done)
<-done
}
func (s SignalSource) WaitAll(count int) {
done := make(chan bool)
defer close(done)
go s(done)
for i := 0; i < count; i++ {
<- done
}
}
68. package generalise
type Iteration func(k, x interface{})
func (i Iteration) apply(k, v interface{}, c chan bool) {
go func() {
i(k, v)
c <- true
}()
}
70. package generalise
import . "reflect"
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{}) {
s = f(s, x)
}).Each(c)
r <- s
}()
return
}
86. package main
import . “clock”
func main() {
c := Clock{1000, make(chan int64), make(chan bool), false}
c.Start()
for i := 0; i < 3; i++ {
println("pulse value", <-c.Count, "from clock")
}
println("disabling clock")
c.Control <- false
syscall.Sleep(1000000)
println("restarting clock")
c.Control <- true
println("pulse value", <-c.Count, "from clock")
}
87. OSX 10.6.2 Intel Atom 270 @ 1.6GHz:
pulse value 0 from clock
pulse value 1 from clock
pulse value 2 from clock
disabling clock
restarting clock
pulse value 106 from clock
OSX 10.6.7 Intel Core 2 Duo @ 2.4GHz:
pulse value 0 from clock
pulse value 1 from clock
pulse value 2 from clock
disabling clock
restarting clock
pulse value 154 from clock
93. package instructions
import "fmt"
type Operation func(o []int)
type Executable interface {
Opcode() int
Operands() []int
Execute(op Operation)
}
const INVALID_OPCODE = -1
type Program []Executable
func (p Program) Disassemble(a Assembler) {
for _, v := range p { fmt.Println(a.Disassemble(v)) }
}
94. package instructions
type Instruction []int
func (i Instruction) Opcode() int {
if len(i) == 0 { return INVALID_OPCODE }
return i[0]
}
func (i Instruction) Operands() []int {
if len(i) < 2 { return []int{} }
return i[1:]
}
func (i Instruction) Execute(op Operation) {
op(i.Operands())
}
95. package instructions
type Assembler struct {
opcodes map[string] int
names map[int] string
}
func NewAssember(names... string) (a Assembler) {
a = Assembler{ make(map[string] int), make(map[int] string) }
a.Define(names...)
return
}
func (a Assembler) Define(names... string) {
for _, name := range names {
a.opcodes[name] = len(a.names)
a.names[len(a.names)] = name
}
}
96. package instructions
func (a Assembler) Assemble(name string, params... int) (i Instruction) {
i = make(Instruction, len(params) + 1)
switch opcode, ok := a.opcodes[name]; {
case ok:! i[0] = opcode
!
default:!! i[0] = INVALID_OPCODE
}
copy(i[1:], params)
return
}
97. package instructions
import "fmt"
func (a Assembler) Disassemble(e Executable) (s string) {
if name, ok := a.names[e.Opcode()]; ok {
s = name
if params := e.Operands(); len(params) > 0 {
s = fmt.Sprintf("%vt%v", s, params[0])
for _, v := range params[1:] {
s = fmt.Sprintf("%v, %v", s, v)
}
}
} else {
s = "unknown"
}
return
}
98. package main
import . “instructions”
func main() {
a := NewAssembler("noop", "load", "store")
p := Program{ a.Assemble("noop"),
a.Assemble("load", 1),
a.Assemble("store", 1, 2),
a.Assemble("invalid", 3, 4, 5) }
p.Disassemble(a)
for _, v := range p {
if len(v.Operands()) == 2 {
v.Execute(func(o []int) { o[0] += o[1] })
println("op =", v.Opcode(), "result =", v.Operands()[0])
}
}
}
99. produces:
noop
load! ! 1
store!1, 2
unknown
op = 2 result = 3