Wilson Canda
Developer, python & golang enthusiast.
an intro*
slides.com/wmv/go/live
* (superficial)
“Go is not meant to innovate programming theory. It’s meant to innovate programming practice.”
– Samuel Tesla
Sep 2007: R Griesemer, R Pike, K Thompson dream up GO
Nov 2009: GO officially announced
May 2010: Start of GO internal use @ Google
Mar 2012: GO v1 is released
not a very old language at all!
Authors:
it's designed to help write big programs, written and maintained by big teams.
... why stay ?
Lots of big players like it!
$ mkdir $HOME/go
$ export GOPATH=$HOME/go
$ export PATH=$PATH:$GOPATH/bin
( ... vim/emacs have great support too )
package main
import "fmt"
func main() {
// Kon'nichiwa!
fmt.Println("こんにちわ, Go!")
}
package main
import (
"fmt"
)
func main() {
a, b := 1, 2
fmt.Printf("a + b: %d\n", a + b)
for a < 100 {
a++
fmt.Println(a)
}
}
package main
import (
"fmt"
"math"
)
func main() {
for i := 0; i < 100; i++ {
fmt.Println(math.Pow(float64(i), 2))
}
}
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Printf("Hey! New request!")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Same syntax for methods, anonymous functions & closures
main.main() is the function run when the program is executed
No void
No return value
No function args (command line is in os package)
Not classes or objects
No subpackages
Fundamental building block in Go
Programs run “main” package
Package path is just a string (allows for URLs)
Standard library sits at the root (no path)
private vs Public functions, types, etc =
= Caps vs noCaps
package main
import "fmt"
func main() {
for i := 1; i<=5; i++ {
fmt.Printf("Welcome %d times\n",i)
}
}
(similar to Java, C)
package main
import "fmt"
func main() {
sum := 1
for ; sum < 1000; { // No pre or post
sum += sum
}
fmt.Println(sum)
}
As in C or Java, you can leave the pre and post statements empty.
package main
import "fmt"
func main() {
sum := 1
for sum < 1000 { // No semicolons
sum += sum
}
fmt.Println(sum)
}
package main
func main() {
for { // Runs forever!
}
}
(while true)
if conditions are omitted, block loops forever
package main
import "fmt"
func main() {
// Here's a basic example.
if 7%2 == 0 {
fmt.Println("7 is even")
} else {
fmt.Println("7 is odd")
}
// You can have an `if` statement without an else.
if 8%4 == 0 {
fmt.Println("8 is divisible by 4")
}
// A statement can precede conditionals; any variables
// declared in this statement are available in all
// branches.
if num := 9; num < 0 {
fmt.Println(num, "is negative")
} else if num < 10 {
fmt.Println(num, "has 1 digit")
} else {
fmt.Println(num, "has multiple digits")
// _Switch statements_ express conditionals across many
// branches.
package main
import "fmt"
import "time"
func main() {
// Here's a basic `switch`.
i := 2
fmt.Print("write ", i, " as ")
switch i {
case 1:
fmt.Println("one")
case 2:
fmt.Println("two")
case 3:
fmt.Println("three")
}
// You can use commas to separate multiple expressions
// in the same `case` statement. We use the optional
// `default` case in this example as well.
switch time.Now().Weekday() {
case time.Saturday, time.Sunday:
fmt.Println("it's the weekend")
default:
fmt.Println("it's a weekday")
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.", os)
}
}
What's your OS?
package main
import "fmt"
func main() {
defer fmt.Println("World!")
fmt.Println("Hello, ")
}
package main
import "fmt"
func main() {
defer func1()
defer func2()
defer func3()
defer func4()
fmt.Println("Check out how these execute:")
}
func func1() {
print("Outer Func\n")
}
func func2() {
print("1 level in\n")
}
func func3() {
print("2 levels in\n")
}
func func4() {
print("3 levels in\n")
}
Stack
(LIFO)
package main
import "fmt"
func main() {
letters := []string{"Aa", "Bb", "Cc", "Dd", "Ee"}
for i, v := range letters {
fmt.Printf("%d = %s\n", i, v)
}
}
package main
import "fmt"
func f(from string) {
for i := 0; i < 3; i++ {
fmt.Println(from, ":", i)
}
}
func main() {
// Suppose we have a function call `f(s)`. Here's how
// we'd call that in the usual way, running it
// synchronously.
f("direct")
// To invoke this function in a goroutine, use
// `go f(s)`. This new goroutine will execute
// concurrently with the calling one.
go f("goroutine")
// You can also start a goroutine for an anonymous
// function call.
go func(msg string) {
fmt.Println(msg)
}("going")
A goroutine is a lightweight thread of execution.
Channels are the pipes that connect concurrent goroutines.
You can send values into channels from one goroutine and receive those values into another goroutine.
A channel type is represented with the keyword chan followed by the type of the things that are passed on the channel
The <- (left arrow) operator is used to send and receive messages on the channel.
package main
import "fmt"
func main() {
// Create a new channel with `make(chan val-type)`.
// Channels are typed by the values they convey.
messages := make(chan string)
// _Send_ a value into a channel using the `channel <-`
// syntax. Here we send `"ping"` to the `messages`
// channel we made above, from a new goroutine.
go func() { messages <- "ping" }()
// The `<-channel` syntax _receives_ a value from the
// channel. Here we'll receive the `"ping"` message
// we sent above and print it out.
msg := <-messages
fmt.Println(msg)
}
& in front of variable name is used to retrieve the address of where this variable’s value is stored. That address is what the pointer is going to store.
* in front of a variable of pointer type is used to retrieve a value stored at given address. In Go speak this is called dereferencing.
* in front of a type name, means that the declared variable will store an address of another variable of that type (not a value of that type).
package main
import "fmt"
func main() {
i, j := 42, 2701
p := &i // point to i
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
*p = *p / 37 // divide j through the pointer
fmt.Println(j) // see the new value of j
}
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
v.X = 4
fmt.Println(v.X)
}
package main
import "fmt"
import "math"
type geometry interface {
area() float64
perim() float64
}
type square struct {
width, height float64
}
type circle struct {
radius float64
}
func (s square) area() float64 {
return s.width * s.height
}
func (s square) perim() float64 {
return 2*s.width + 2*s.height
}
func (c circle) area() float64 {
return math.Pi * c.radius * c.radius
}
func (c circle) perim() float64 {
return 2 * math.Pi * c.radius
}
func measure(g geometry) {
fmt.Println(g)
fmt.Println(g.area())
fmt.Println(g.perim())
}
func main() {
s := square{width: 3, height: 4}
c := circle{radius: 5}
measure(s)
measure(c)
}
more on OOP:
An array's length is part of its type, so arrays cannot be resized.
Mostly interacted with upon instantiation only.
An array is not a slice.
The slice type is an abstraction built on top of Go's array type
A slice points to an array of values and also includes a length.
It is common to append new elements to a slice, and so Go provides a built-in append function.
TL;DR:
You’ll see slices much more often than arrays in typical Go.
Arrays have their place, but they're a bit inflexible, so you don't see them too often in Go code. Slices, though, are everywhere. They build on arrays to provide great power and convenience.
An array literal can be specified like so:
b := [2]string{"Penn", "Teller"}
A slice literal is declared just like an array literal, except you leave out the element count:
letters := []string{"a", "b", "c", "d"}
This is also the syntax to create a slice given an array:
x := [3]string{"Лайка", "Белка", "Стрелка"}
s := x[:] // a slice referencing the storage of x
package main
import "fmt"
func main() {
var a [2]string
a[0] = "Hello"
a[1] = "World"
fmt.Println(a[0], a[1])
fmt.Println(a)
}
Tip: The type [n]T is an array of n values of type T.
package main
import "fmt"
func main() {
p := []int{2, 3, 5, 7, 11, 13}
fmt.Println("p ==", p)
for i := 0; i < len(p); i++ {
fmt.Printf("p[%d] == %d\n", i, p[i])
}
}
Tip: The length and capacity of a slice can be inspected using the built-in len and cap functions.
package main
import "fmt"
func main() {
var timeZone = map[string]int{
"UTC": 0,
"EST": -5,
"CST": -6,
"MST": -7,
"PST": -8,
}
fmt.Println(timeZone["EST"])
timeZone2 := make(map[string]int)
timeZone2["XST"] = 4
fmt.Println(timeZone2["XST"])
}
Maps are Go’s built-in associative data type (similar to python dicts).
By convention, errors are the last return value and have type error, a built-in interface.
A nil value in the error position indicates that there was no error.
“Why would you have a language that is not theoretically exciting? Because it’s very useful.”
— Rob Pike
www.golang-book.com
tour.golang.org
blog.golang.org
talks.golang.org
spf13.com/presentation/go-for-object-oriented-programmers
gobyexample.com
http://commandcenter.blogspot.com/
this deck: slides.com/wmv/go
https://gist.github.com/wmv/1c62bcacbf20f4621daf
to IO (http://io.co.za/) - they are awesome!
Questions?
illustrations here are mostly by Renée French
click to tweet:
By Wilson Canda
Gentle introduction to golang, prepared for a GDG Cape Town meetup.