A motivation to Go(lang)

Daniel Mejía R.

http://daniel-m.github.io

August 2018

What is Go(lang)?

As https://golang.org states,

Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.

  • Developed at Google as early as 2009 by Robert Griesemer, Rob Pike, and Ken Thompson.
  • Version 1.0 Released at 2012.

Notable Pros

  • Exploit multicore and networked machines through concurrency.
  • Compiles way too fast.
  • Garbage collected at compilation time.
  • Strongly typed.
  • Easy management of development environment (Workspace).
  • git oriented development (packages automatically downloaded from remote repos).
  • Native code formatting tool (linter).
  • Package documentation generated at ease with native tools and self. hosted (godoc.org).
  • Package testing with native tools.
  • Race condition analyzer.
  • Multi-platform (Operating systems).
  • Multi-architecture (Hardware compatibility).
  • Cross compiling as easy as changing an environment variable.
  • Monolithic executables.
  • Runtime reflection (mutable executables at runtime).
  • Intensively documented, standardized.
  • Easily shareable code snippets that execute on the cloud (http://play.golang.org).
  • Great community.

And lots more…

Cons of some sort

  • Non Object Oriented Programming (though it has structures and interfaces).
  • Non functional programming (though it can be emulated through functions or closures).
  • Monolithic executables (lacks the concept of shared library to link against it).
  • Non centralized package server.
  • Depends on external services to provide packages (git forges -> expose security vulnerabilities!).
  • Dependency management still in development (early implementations of vgo to be official within go1.12).

Why Go?: personal perspective

Go helped me with,

  • Adopt proper style: hard to implement brute-force programming.
  • First steps with web-dev.
  • Lots of API libraries to experiment with.
  • Brag about programming with the Google’s language.

First steps

Installing

Go binary install is recommended to have the latest go version from https://golang.org/dl/ following instructions.

The Workspace

Go relies in the concept of workspace. The environment variable GOPATH must be set to an existing directory which will hold all the go packages, binaries and precompiled packages

Sample workspace

bin/
    hello                          # command executable
    outyet                         # command executable
pkg/
    linux_amd64/
        github.com/golang/example/
            stringutil.a           # package object
src/
    github.com/golang/example/
        .git/                      # Git repository metadata
	hello/
	    hello.go               # command source
	outyet/
	    main.go                # command source
	    main_test.go           # test source
	stringutil/
	    reverse.go             # package source
	    reverse_test.go        # test source
    golang.org/x/image/
        .git/                      # Git repository metadata
	bmp/
	    reader.go              # package source
	    writer.go              # package source
    ... (many more repositories and packages omitted) ...

Making the binaries available

Put compiled go packages as console commands is as easy as adding GOPATH to the PATH,

$ export PATH=$GOPATH/bin:$PATH

Installing go packages

To include a package within a workspace,

$ go get github.com/user/hello

Installing a go package goes as,

$ go install github.com/user/hello

Which first <go get> and then compiles the binaries into $GOPATH/bin

Anatomy of a go source file

Open https://play.golang.org/ in your browsers. Throughout the presentations, links to the playground will be provided to see live performance

package main // Every go package starts with the word "package"

// Here come the package imports
import (
	"fmt"
)

// Here comes the main function
func main() { // <- Opening braces must be here 

	// We are using the method "Println"
	// included within the package "fmt"
	fmt.Println("Hello, playground") 
}

Variable declaration

https://play.golang.org/p/HYUpJDXWte0

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	// Variable declaration follows the rule
	// var name type [= initial_value] 
	var greeting string = "Hello, 世界"

	fmt.Println(greeting)

	fmt.Println("My favorite number is", rand.Intn(10))
}

Short Variable Declarations

package main

import (
	"fmt"
	"math/rand"
)

func main() {
	// Variables can be assigned by
	// the ":=" operator which infers the
	// adequate types
	greeting := "Hello, 世界"

	fmt.Println(greeting)

	fmt.Println("My favorite number is", rand.Intn(10))
}

Go Basic types

bool // true, false

string // "Hello World!"

int  int8  int16  int32  int64 // Several flavors of integers
uint uint8 uint16 uint32 uint64 uintptr

byte // alias for uint8. Size guaranteed

rune // alias for int32
     // represents a Unicode code point. Size guaranteed   

float32 float64 // single and double precision

complex64 complex128 // Yes!, complex numbers natively supported

Functions

https://play.golang.org/p/Fy3IxY-LLdI

package main

import (
	"fmt"
	"math/rand"
)

// Greeter returns a greeting string  
func Greeter() string {
	return "Hello, 世界"
}

// Number returns a random int seeded with seed
func Number(seed int) int {
	return rand.Intn(seed)
}

func main() {

	fmt.Println(Greeter())

	fmt.Println("My favorite number is", Number(10))
}

Functions with multiple return

https://play.golang.org/p/yUPdKi-zeHL

package main

import (
	"fmt"
	"math/rand"
)

// GreeterNumber returns a greeting string 
// and a number.
// Yes, go supports multiple return functions
func GreeterNumber(seed int) (string, int) {
	return "Hello, 世界", rand.Intn(seed)
}

func main() {
	// Variables can be assigned by
	// the ":=" operator which infers the
	// adequate types
	greet, num := GreeterNumber(10)
	fmt.Println(greet)

	fmt.Println("My favorite number is", num)
}

Defer

“Defers” function call

https://play.golang.org/p/TD6SZBXwn_r

package main

import "fmt"

func main() {

	defer fmt.Println("print last...")
	defer fmt.Println("You didn't expected me to")
	defer fmt.Println("I bet,")
	
	fmt.Println("Hey there!")
	fmt.Println("Ready to see something amazing?")
}

Go conditionals

If statement

https://play.golang.org/p/g5TY4wb916e

package main

import (
	"fmt"
)

func abs(x float64) float64 {
	if x < 0 {
		return abs(-x)
	}
	return x
}

func main() {
	fmt.Println(abs(2), abs(-4))
}

If else

https://play.golang.org/p/zqAHNoFiUZO

package main

import (
	"fmt"
)

func abs(x float64) float64 {
	if x < 0 {
		return abs(-x)
	} else {
		return x
  }
}

func main() {
	fmt.Println(abs(2), abs(-4))
}

If else-if else


func abs(x float64) float64 {
	if x < 0 {
		return abs(-x)
	} else if x == 0 {
		return 0
	} else {
		return x
  }
}

Go Loops

A single loop structure rules them all,

https://play.golang.org/p/HwJ5nXetGDV

// single condition for looping
i := 1
for i <= 3 {
    fmt.Println(i)
    i = i + 1
}

Go Loops

A single loop structure rules them all,

https://play.golang.org/p/HwJ5nXetGDV

// A classic C-like initial/condition/after `for` loop.
for j := 7; j <= 9; j++ {
    fmt.Println(j)
}

Go Loops

A single loop structure rules them all,

https://play.golang.org/p/HwJ5nXetGDV

// `for` without a condition will loop repeatedly
// until you `break` out of the loop or `return` from
// the enclosing function.
for {
    fmt.Println("loop")
    break
}

Go Loops

A single loop structure rules them all,

https://play.golang.org/p/HwJ5nXetGDV

// You can also `continue` to the next iteration of
// the loop.
for n := 0; n <= 5; n++ {
    if n%2 == 0 {
        continue
    }
    fmt.Println(n)
}

Arrays

https://play.golang.org/p/A1oG39orUpZ

  • Arrays are of fixed size.
  • The type [n]T is an array of n values of type T.
func main() {
	var a [2]string
	a[0] = "Hello"
	a[1] = "World"
	fmt.Println(a[0], a[1])
	fmt.Println(a)

	// Or the short declaration
	// also known as "array literal"
	primes := [6]int{2, 3, 5, 7, 11, 13}
	fmt.Println(primes)
}

Slices

  • A slice is a dynamically-sized.
  • The type []T is a slice with elements of type T.
  • A slice is formed by specifying two indices, a low and high bound, separated by a colon: a[low : high] high is not included.
primes := [6]int{2, 3, 5, 7, 11, 13}
var s []int = primes[1:4]
fmt.Println(s) // Outputs: [3 5 7]

Slices and make operator

https://play.golang.org/p/ZwE_3e063GG

  • The make function allocates a zeroed array and returns a slice that refers to that array:
a := make([]int, 5)  // len(a)=5
fmt.Printf("len=%d cap=%d %v\n", len(a), cap(a), a)
// Outputs:
// len=5 cap=5 [0 0 0 0 0]

b := make([]int, 0, 5)
fmt.Printf("len=%d cap=%d %v\n", len(b), cap(b), b)
// Outputs:
// b len=0 cap=5 []

Maps

https://play.golang.org/p/-axSPJOLI1A

  • The make function returns a map of the given type, initialized and ready for use.
// Create an array of float64
udea := [2]float64{6.2554189, -75.5920199}

// Allocate a map from string to [2]float64
sitios := make(map[string][2]float64)

// assign element to map
sitios["udea"] = udea
fmt.Println(sitios)
// Outputs :[6.2554189 -75.5920199]]

Range operator for slices

https://play.golang.org/p/NtNGEbZokIX

  • The range form of the for loop iterates over a slice or map.
  • When ranging over a slice, two values are returned: the first is the index, and a copy of the element at that index.
var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}
for i, v := range pow {
	fmt.Printf("2**%d = %d\n", i, v)
}

Range operator for maps

https://play.golang.org/p/WmxXd4xsftV

  • When ranging over a map, two values are returned for each iteration. The first is the key, and the second is a copy of the element at that key.
var pow = map[int]int{0:1, 1:2, 2:4, 3:8}
for k, v := range pow {
	fmt.Printf("2**%d = %d\n", k, v)
}

Anonymous functions (lambdas)

  • Functions can be passed around just like other values, and may be used as function arguments and return values.
func compute(fn func(float64, float64) float64) float64 {
	return fn(3, 4)
}

func main() {
	hypot := func(x, y float64) float64 {
		return math.Sqrt(x*x + y*y)
	}
	fmt.Println(hypot(5, 12))

	fmt.Println(compute(hypot))
	fmt.Println(compute(math.Pow))
}

Pointers

Go has pointers, but lack pointer arithmetic https://play.golang.org/p/O0qc2lYGuAK

package main

import "fmt"

func main() {
	i, j := 42, 2701

	p := &i         // point to i
	fmt.Println(p)  // who is p?
	fmt.Println(*p) // read i through the pointer
	*p = 21         // set i through the pointer
	fmt.Println(i)  // see the new value of i

	p = &j         // point to j
	fmt.Println(p)  // who is p now?
	*p = *p / 37   // divide j through the pointer
	fmt.Println(j) // see the new value of j
}

Pointer Syntax

The type *T is a pointer to a T value. Its zero value is nil.

var p *int // Will store a pointer (nil)

The & operator generates a pointer to its operand.

i := 42 // Short int declaration
p = &i // p now points to i

The * operator denotes the pointer’s underlying value.

fmt.Println(*p) // read i through the pointer p
+p = 21         // set i through the pointer p

This is known as “dereferencing” or “indirecting”.

Structures

https://play.golang.org/p/zQWVtBPH3-w

// We declare the Person structure
type Person struct {
	Name string
	LastN string
	Email string
}

func main() {
	john := Person{"John","Doe", "john@doe.jd"}
	fmt.Println("The person is", john)
}

Structure Methods

https://play.golang.org/p/OdmLz3h-AbI

// Mumble returns a greeting string
func (p *Person) Mumble() string {
	return "Hey, my name is " + p.Name + " " + p.LastN
}

// Speak Mumbles to standard output
func (p *Person) Speak() {
	fmt.Println(p.Mumble())
}

Structure Methods

https://play.golang.org/p/OdmLz3h-AbI

func main() {

	// Struct literal for John
	john := Person{"John", "Doe", "john@doe.jd"}

	// We can see that the standard output handles
	// Data streaming easily (Not overload required)
	fmt.Println("The person is", john)

	// The struct is edowed with two methods
	fmt.Println(john.Mumble())

	// Tell'em John!
	john.Speak()
}

Good practices with structures

Create Structure instances with New... functions,

type Something {
  ...
}

  ...

func NewSomething(...) *Something {
 ...
}

Good practices with structures

Create Structure instances with New... functions,

type Something {
  ...
}

  ...

func NewSomething(...) *Something {
 ...
}

Interfaces

https://play.golang.org/p/a_DtRz6gvub

  • An interface type is defined as a set of method signatures.
// Cow structure
type Cow struct {
	Color string
}

// Cow makes "moo"
func (c *Cow) MakeSound() {
	fmt.Println("Moo")
}

Interfaces

https://play.golang.org/p/a_DtRz6gvub

// Pigeon structure
type Pigeon struct {
	Age int
}

// Pidgeon makes "Coo"
func (p *Pigeon) MakeSound() {
	fmt.Println("Coo")
}

Interfaces

https://play.golang.org/p/a_DtRz6gvub

// Dog structure
type Dog struct {
	Age   int
	Color string
	Name  string
}

// Dogs bark
func (d *Dog) MakeSound() {
	fmt.Println("Bark!")
}

// Dogs can roll too
func (d *Dog) Roll() {
	fmt.Println("Dog", d.Name, "rolls over")
}

Interfaces

https://play.golang.org/p/a_DtRz6gvub

  • Interfaces are implemented implicitly, no keywords needs to be added, if interface methods are implemented, the interface implemented.
// Sounder groups all types which can MakeSound
type Sounder interface {
	MakeSound()
}

Interfaces

https://play.golang.org/p/a_DtRz6gvub

// Interfaces are types
// We can instance a Sounder
var animal Sounder

// Pidgeons comply the interface
animal = &Pigeon{1}

animal.MakeSound()

// Cows comply the interface
animal = &Cow{"black"}

animal.MakeSound()

// Dogs comply the interface
animal = &Dog{3, "Black and White", "Max"}
animal.MakeSound()

Concurrency: Go routines

https://play.golang.org/p/2V3uawcTh3K

  • A goroutine is a lightweight thread managed by the Go runtime.
  • The syntax is a simple as go function_call().

Concurrency: Go routines

https://play.golang.org/p/2V3uawcTh3K

// One prints Hello 10 times
func One() {
	for i := 0; i < 10; i++ {
		fmt.Println("Hello")
	}
}

// Two prints World 10 times
func Two() {
	for i := 0; i < 10; i++ {
		fmt.Println("World")
	}
}

Concurrency: Go routines

https://play.golang.org/p/2V3uawcTh3K

func main() {

	// Starting a couple of go routines
	go Two()
	go One()
	// Uncomment below :)
	// go time.Sleep(3 * time.Second)
	time.Sleep(3 * time.Second)
}

Concurrency: Channels

  • Channels are a typed conduit through which you can send and receive values with the channel operator, <-.
ch <- v    // Send v to channel ch.
v := <-ch  // Receive from ch, and
           // assign value to v.
  • The data flows in the direction of the arrow.

Concurrency: Channels

Like maps and slices, channels must be created before use:

ch := make(chan int)
  • By default, sends and receives block until the other side is ready. This allows goroutines to synchronize without explicit locks or condition variables.
// Greet sends "Hello World" through a channel
func Greet(c chan string) {
	
	// Text to be sent
	greet := "Hello world"
	
	// Loop over the string
	for _, v := range greet {
		// send each character on the channel
		c <- string(v)
	}
	
	// Close the channel once the operation is done
	close(c)
}

// We need to create the channel first
c := make(chan string)

// Go routine for the Greet function
go Greet(c)

// Iterate over the input channel
for i := range c {
	fmt.Println(i)
}

Documenting the code

  • Documenting the code requires a special formatting.
  • The documentation is generated automatically by godoc tool.
// LogFormatter initiates the beginning of a new LogEntry per request.
// See DefaultLogFormatter for an example implementation.
type LogFormatter interface {
	NewLogEntry(r *http.Request) LogEntry
}

// LogEntry records the final log when a request completes.
// See defaultLogEntry for an example implementation.
type LogEntry interface {
	Write(status, bytes int, elapsed time.Duration)
	Panic(v interface{}, stack []byte)
}

Testing

https://play.golang.org/p/DlJRghK5eRQ

  • Part of the standard library "testing"
  • Executed with the command go test
  • File naming convention package_test.go
import (
	"testing"
)

// Squared squares integer numbers
func Squared(number int) int {
	return number * number
}

Testing

https://play.golang.org/p/DlJRghK5eRQ

  • For sake of example, lets define a struct
// testCase is a struct for the test cases
type testCase struct {
	input int
	wants int
}

Testing

https://play.golang.org/p/DlJRghK5eRQ

// TestSquared tests the function Squared
func TestSquared(t *testing.T) {
	testCases := []testCase{{2, 4}, {3, 9}, {10, 100}, {5, 25}}
	// Uncomment below :)
	//testCases = []testCase{{2, 4}, {3, 9}, {10, 100}, {5, 25}, {2,2}}

	// Iterate over the testCases slice
	for _, test := range testCases {
		if Squared(test.input) != test.wants {
			t.Errorf("Failed test, expected %d got %d", test.wants, Squared(test.input))
		}
	}
}

Advanced topics

  • Logging with "log".
  • Input/Output operations a.k.a. Writters and Readers.
  • Buffered channels.
  • "sync" package to synchronize go routines.
  • Testing patterns with Go.
  • Interfaces, Pointers and Structures.
  • vgo and package versioning.
  • Go routine channel select operation.

Final Words…

Thanks for your time