1 of 14

Go for Python users

Pyvo, 15. 7. 2020

2 of 14

What?

  • A language out of Google
  • First released in 2009, 1.0 in 2012
    • no major changes since
  • Statically typed
  • Compiled
  • Garbage collected
  • Fast-ish
  • Concurrent
  • Popular for servers, CLIs, tooling
    • Docker, Kubernetes
    • Istio, etcd, Traefik
    • Prometheus, InfluxDB
    • Terraform (all of HashiCorp), Hugo

3 of 14

Why?

  • Simple, stable, popular
  • Typed
  • Fast-ish
  • No VM or interpreter, garbage collected
  • Quick compilation (go run program.go), single binary
  • Builtin tooling
  • Concurrency primitives
    • Green threads, message passing
  • Decent standard library
  • No exceptions
  • Good ecosystem

4 of 14

Why not?

  • Garbage collected
  • Suboptimal C interoperability
  • Limited in performance
    • Own compiler
    • Can’t control some allocations
    • No native binaries
  • No generics (yet)
  • Weird Google governance
  • All of these things are solved in Rust

5 of 14

Contra Python

  • Why
    • Faster*
    • Statically built and cross compiled
    • Type safe
  • Why not
    • More verbose - lacks iterators, decorators, sets; types; allocators
    • Fewer external libraries
    • Less popular
    • No REPL
  • Meh
    • Verbose error handling
    • No exceptions
    • Different governance model

6 of 14

Sum ints in a file

func sumInts(filename string) (int, error) {

f, err := os.Open(filename)

if err != nil {

return 0, err

}

defer f.Close()

scanner := bufio.NewScanner(f)

sum := 0

for scanner.Scan() {

num, err := strconv.Atoi(scanner.Text())

if err != nil {

return 0, err

}

sum += num

}

if scanner.Err() != nil {

return 0, scanner.Err()

}

return sum, nil

}

7 of 14

Run it on some files

func run() error {

filenames, err := filepath.Glob("data/*.txt")

if err != nil {

return err

}

for _, filename := range filenames {

sum, err := sumInts(filename)

if err != nil {

return err

}

fmt.Println(sum)

}

return nil

}

8 of 14

Run it concurrently

func run() error {

filenames, err := filepath.Glob("data/*.txt")

if err != nil {

return err

}

for _, filename := range filenames {

go func(filename string) {

sum, err := sumInts(filename)

if err != nil {

panic(err)

}

fmt.Println(sum)

}(filename)

}

return nil

}

9 of 14

Run it concurrently and wait for it

var wg sync.WaitGroup

wg.Add(len(filenames))

for _, filename := range filenames {

go func(filename string) {

defer wg.Done()

sum, err := sumInts(filename)

if err != nil {

panic(err)

}

fmt.Println(sum)

}(filename)

}

wg.Wait()

10 of 14

Communicate instead of waiting

results := make(chan int)

for _, filename := range filenames {

go func(filename string) {

sum, err := sumInts(filename)

if err != nil {

panic(err)

}

results <- sum

}(filename)

}

for j := 0; j < len(filenames); j++ {

fmt.Println(<-results)

}

11 of 14

Launch workers and subscribe to work

nworkers := 2

work := make(chan string) // filenames

results := make(chan int)

for j := 0; j < nworkers; j++ {

go func() {

for filename := range work {

sum, err := sumInts(filename)

if err != nil {

panic(err)

}

results <- sum

}

}()

}

12 of 14

Send the work and receive results

done := make(chan bool)

go func() {

for j := 0; j < len(filenames); j++ {

fmt.Println(<-results)

}

close(results)

close(work)

done <- true

}()

for _, filename := range filenames {

work <- filename

}

<-done

13 of 14

Links

14 of 14

Thank you