A Recovering Java DeveloperLearns to Go Matt Stine (@mstine) Cloud Foundry Platform Engineer at Pivotal matt.stine@gmail.com http://www.mattstine.com OFFICE HOURS Wednesday, 2:30 - 3:10 PM Expo Hall (Table A)
CF Architecture -January 2013 DEA Pool Router Cloud Controller BOSH Director BOSH Agent UAA/Login Servers Health Manager Service Broker Node(s) Messaging (NATS) CLI Client Ruby Java/Spring Go
CF Architecture -January 2014 Ruby Java/Spring Go Loggregator DEA Pool (Diego - Coming soon!) Router Cloud Controller BOSH Director BOSH Agent UAA/Login Servers Health Manager Service Broker Node(s) Messaging (NATS) CLI Client
• Mike Gehard:“Go Within Cloud Foundry” https://www.youtube.com/watch?v=d5aHr8VGU-8 • Onsi Fakhouri: “Diego: Re-envisioning the Elastic Runtime” https://www.youtube.com/watch?v=1OkmVTFhfLY Go in Cloud Foundry
✓ Hello World ✓Why Go? ✓ Contrasts with Java: - Features and Idioms - Packaging / Modularity - Types / OOP / Interfaces - Concurrency Agenda
package main ! import ( "fmt" ) ! funcmain() { fmt.Println("Hello World") } Hello World All code goes in a package. Give access to exported stuff from other packages. Function definition, main() is entrypoint. Call an exported function!
Iron Triangle ofLanguage Design EfficientCompilation Ease of Programming EfficientExecution Systems Programming
• 2007-09-21: Goinvented at Google by Robert Griesemer, Rob Pike, and Ken Thompson • 2009-11-10: Go released as OSS • 2012-03-28: Go 1.0 is Released A History Lesson
Software Engineering inthe LARGE http://talks.golang.org/2012/splash.article “Go is a programming language designed by Google to help solve Google's problems, and Google has big problems.”
Software Engineering inthe LARGE http://talks.golang.org/2012/splash.article “Go is a programming language designed by Google to help solve Google's problems, and Google has big problems.”
✓ Safety andefficiency of a statically-typed, compiled language ✓ Productivity and feel of a dynamic, interpreted language ✓ Address modern compute environments: - Multicore Processors - Networked Systems - Massive Computational Clusters - Web Programming Model Goals
• Features andIdioms • Packaging / Modularity • Types / OOP / Interfaces • Concurrency Contrasts with Java
Multiple Return Values funcvals() (int, int) { return 3, 7 } ! func main() { a, b := vals() fmt.Println(a) fmt.Println(b) ! _, c := vals() fmt.Println(c) } GBE Return a pair of values. I Ignore the first value returned. Assign to multiple variables.
Closures func main() { nextInt:= intSeq() ! fmt.Println(nextInt()) fmt.Println(nextInt()) fmt.Println(nextInt()) ! newInts := intSeq() fmt.Println(newInts()) } GBE Captures its own value for i. Increments own value of i. Captures the value of i again! And increments it.
Where’s my java.util.List?Slices s := make([]string, 3) fmt.Println("emp:", s) ! s[0] = "a" s[1] = "b" s[2] = "c" fmt.Println("set:", s) fmt.Println("get:", s[2]) ! s = append(s, "d") s = append(s, "e", "f") fmt.Println("apd:", s) GBE Create an empty slice of strings (zero-valued). Set value at index. Get value at index. Append function (not mutate in-place!).
Where’s my java.util.List?Slices c := make([]string, len(s)) copy(c, s) fmt.Println("cpy:", c) ! l := s[2:5] fmt.Println("sl1:", l) ! l = s[:5] fmt.Println("sl2:", l) ! t := []string{"g", "h", "i"} fmt.Println("dcl:", t) GBE Length function. Copy function. Slicing function: index 2 (inclusive) to index 5 (exclusive). Slicing function: index 0 (inclusive) to index 5 (exclusive). Slice literals!
Where’s my java.util.Map?Maps m := make(map[string]int) ! m["k1"] = 7 m["k2"] = 13 ! fmt.Println("map:", m) ! v1 := m["k1"] fmt.Println("v1: ", v1) ! fmt.Println("len:", len(m)) Create an empty map of string ! int. Put values. Get value. Length function. GBE
Where’s my java.util.Map?Maps delete(m, "k2") fmt.Println("map:", m) ! _, prs := m["k2"] fmt.Println("prs:", prs) ! n := map[string]int{"foo": 1, "bar": 2} fmt.Println("map:", n) Delete function. Optional second return indicating “presence.” Map literals! GBE
Looping with Range nums:= []int{2, 3, 4} sum := 0 for _, num := range nums { sum += num } fmt.Println("sum:", sum) ! for i, num := range nums { if num == 3 { fmt.Println("index:", i) } } ! kvs := map[string]string{"a": "apple", "b": "banana"} for k, v := range kvs { fmt.Printf("%s -> %sn", k, v) } Discard first (index), sum second (value). Keep both returns! With maps, first = key, second = value. GBE
We don’t needno stinkin’ exceptions… func f1(arg int) (int, error) { if arg == 42 { return -1, errors.New("can't work with 42") } return arg + 3, nil } ! func main() { for _, i := range []int{7, 42} { if r, e := f1(i); e != nil { fmt.Println("f1 failed:", e) } else { fmt.Println("f1 worked:", r) } } } Conventional: last return is error. Makes an error with the provided message. Return nil if there was no error. Idiomatic inline error check. GBE
(Semi)automatic Resource Management funccreateFile(p string) *os.File { fmt.Println("creating") f, err := os.Create(p) if err != nil { panic(err) } return f } func writeFile(f *os.File) { fmt.Println("writing") fmt.Fprintln(f, "data") ! } ! func closeFile(f *os.File) { fmt.Println("closing") f.Close() } GBE
• Every classin a package • Import classes explicitly - import java.util.Map • Import all classes in a package - import java.util.* • Statically import class static members: - import static java.lang.Math.PI - import static java.lang.Math.* Java Packaging
• All typesand functions belong to a package. • Every source file must declare its package. • Import packages to gain access to exported members. Go Packaging
• public -any class in same package, or any importing class in a different package, can see • default (“package private”) - any class in same package can see • protected - any class in same package, or any subclass in a different package, can see • private - no class other than this can see • Scope indicated by prefixing name at declaration time. Java Scoping
• exported -any code in an importing file can see - exported names start with uppercase letter - func Copy(src *[]byte, dest *[]byte) • non-exported - only code in the same package can see - non-exported names start with _ or lowercase letter - func copy(src *[]byte, dest *[]byte) - func _Copy(src *[]byte, dest *[]byte) Go Scoping
• Conventional correspondenceto directory paths (e.g. com.ms.foo should be at src/com/ms/foo) - tools expect this! • Package paths do not have to be unique at compile or runtime (first dependency found/loaded wins!) • Conventional correspondence to URL of author (e.g. my domain is www.mattstine.com, so my packages names start with com.mattstine) - but no actual relationship to source code location! Java Naming
• Conventional correspondenceto directory paths (e.g. github.com/go-martini/ martini should be at src/github.com/go-martini/martini) - tools expect this! • Package paths MUST BE UNIQUE across a $GOPATH. • Package names do not have to be unique. • Referring to imported names must be qualified by package name (e.g. sql.DB not just DB)… can locally alias (e.g. import dbstuff “database/sql”) • Conventional correspondence to URL of code location (e.g. import http://github.com/ joefitzgerald/cfenv as import “github.com/joefitzgerald/cfenv"). • Can “go get” remote packages - supports Git, SVN, Mercurial, Bazaar. Go Naming
• Java admits: -circular package dependencies - dead imports • Go rejects: - circular package dependencies - dead imports Miscellany
structs FTW type Pointstruct { X, Y float64 } Define a type. Give it a name. This type is a struct. (you can actually define others!) Add stuff! (upcase exports apply here too!)
Methods are Functions! func(p Point) Translate(xDist float64, yDist float64) Point { return Point{p.X + xDist, p.Y + yDist} } Receiver argument! Can define methods on pointers or values.
composition FTW type Pointstruct { X, Y float64 } ! const ( BLUE = iota RED = iota GREEN = iota ) ! type ColorPoint struct { Point Point Color int } Define an enumerated constant (closest to Java enum). A ColorPoint has-a Point!
• I havePoints. • I have ColorPoints. • ColorPoints are like Points, but they are not Points. • But I want to compute the euclidean distance between them. • What to do? Problem
Interfaces Group Behaviors typePositioner interface { Coordinates() Point } ! type Distancer interface { DistanceTo(p Positioner) float64 }
It’s all aboutsatisfaction… Java = explicit ! Go = implicit
Calculating Distance func distanceBetween(aPositioner, b Positioner) float64 { p := a.Coordinates() q := b.Coordinates() sqOfXDist := math.Pow(p.X-q.X, 2) sqOfYDist := math.Pow(p.Y-q.Y, 2) return math.Sqrt(sqOfXDist + sqOfYDist) }
Point Satisfies Distancerand Positioner func (p Point) Coordinates() Point { return p } ! func (p Point) DistanceTo(pos Positioner) float64 { return distanceBetween(p, pos) }
ColorPoint Satisfies Distancerand Positioner func (cp ColorPoint) Coordinates() Point { return cp.Point } ! func (cp ColorPoint) DistanceTo(pos Positioner) float64 { return distanceBetween(cp, pos) }
Animal Satisfies Distancerand Positioner func (a Animal) Coordinates() point.Point { return point.Point{X: a.X, Y: a.Y} } ! func (a Animal) DistanceTo(pos point.Positioner) float64 { thing := pos.Coordinates() sqOfXDist := math.Pow(a.X-thing.X, 2) sqOfYDist := math.Pow(a.Y-thing.Y, 2) return math.Sqrt(sqOfXDist + sqOfYDist) }
Go! p = point.Point{X:1, Y: 2} q := point.ColorPoint{Point: point.Point{X: 1, Y: 4}, Color: point.BLUE} ! fmt.Printf("Dist b/w p and q = %vn", p.DistanceTo(q)) fmt.Printf("Dist b/w q and p = %vn", q.DistanceTo(p)) ! penguin := animal.Animal{Name: "penguin", X: 1, Y: 1} seal := animal.Animal{Name: "seal", X: 1, Y: 4} ! fmt.Printf("Dist b/w penguin and seal = %vn", penguin.DistanceTo(seal)) fmt.Printf("Dist b/w penguin and point = %vn", penguin.DistanceTo(p))
• Parallelism =leveraging simultaneous execution of work to perform many things at once. Limited to number of processors/cores you have. • Concurrency = composition of work to manage many things at once. No theoretical limit. • Rob Pike: “Concurrency is Not Parallelism” http://www.youtube.com/watch?v=cN_DpYBzKso Concurrency vs Parallelism
• Java - Threads -OS managed - Share address space with other threads in same process • Go - Goroutines - user-space managed by language runtime - multiplexed onto pool of OS threads Parallelism - How?
• Java - Sharedmemory - Locking • Go - Can share memory (see http://golang.org/pkg/sync) - But there is a better way! Synchronization?
Goroutines func f(from string){ for i := 0; i < 3; i++ { fmt.Println(from, ":", i) } } ! func main() { f("direct") ! go f("goroutine") ! go func(msg string) { fmt.Println(msg) }("going") } GBE Synchronous Asynchronous Asynchronous and Anonymous
Channels func main() { messages:= make(chan string) ! go func() { messages <- "ping" }() ! msg := <-messages fmt.Println(msg) } GBE Create a new channel. Sending Receiving
Channel Buffering func main(){ messages := make(chan string, 2) ! messages <- "buffered" messages <- "channel" ! fmt.Println(<-messages) fmt.Println(<-messages) } GBE Make a channel that will buffer two values. Send twice Receive twice
Channel Synchronization func worker(donechan bool) { fmt.Print("working...") time.Sleep(time.Second) fmt.Println("done") done <- true } ! func main() { done := make(chan bool, 1) go worker(done) <-done } GBE Notify receive that I’m done. Run worker on a goroutine, pass “done” channel. Block until msg received!
Select c1 := make(chanstring) c2 := make(chan string) ! go func() { time.Sleep(time.Second * 1) c1 <- "one" }() go func() { time.Sleep(time.Second * 2) c2 <- "two" }() GBE Create two channels. Create two goroutines; each sends message to different channel.
Select for i :=0; i < 2; i++ { select { case msg1 := <-c1: fmt.Println("received", msg1) case msg2 := <-c2: fmt.Println("received", msg2) } } GBE Await both messages simultaneously! Print each as it arrives!
Closing Channels jobs :=make(chan int, 5) done := make(chan bool) ! go func() { for { j, more := <-jobs if more { fmt.Println("received job", j) } else { fmt.Println("received all jobs") done <- true return } } }() GBE Job channel for sending work. Done channel to indicate all work complete. Receive jobs - more will be false if jobs is closed. If no more jobs, say that I’m done!
Closing Channels for j:= 1; j <= 3; j++ { jobs <- j fmt.Println("sent job", j) } close(jobs) fmt.Println("sent all jobs") ! <-done GBE Send the jobs to the worker. Close the jobs channel. Block until the worker is finished.
Range Over Channels funcmain() { queue := make(chan string, 2) queue <- "one" queue <- "two" close(queue) ! for elem := range queue { fmt.Println(elem) } } GBE Pull messages off channel for each iteration of the loop.
• Features andIdioms • Packaging / Modularity • Types / OOP / Interfaces • Concurrency Contrasts with Java
Thank You!!! Matt Stine(@mstine) Cloud Foundry Platform Engineer at Pivotal matt.stine@gmail.com http://www.mattstine.com OFFICE HOURS Wednesday, 2:30 - 3:10 PM Expo Hall (Table A)
Code samples marked“GBE” at https://gobyexample.com are by Mark McGranaghan and are Creative Commons Attribution 3.0 Unported licensed (http://creativecommons.org/licenses/by/3.0). ! The Go Gopher logo was created by Renee French and is Creative Commons Attribution 3.0 Unported licensed (http:// creativecommons.org/licenses/by/3.0). ! The Java Duke logo is BSD licensed (http://opensource.org/licenses/bsd-license.php).