Why Go?

Go is an application-oriented programming language developed by programmers at Google. One of the major use cases for Go is writing servers, and so much of the language is written to support this.

Much of what follows is based on the Go FAQ. This is a good starting place for learning about the design of Go.

Some of the major features of Go are:

  • Fast compilation. Go is designed from the ground up for writing large, multi-file programs. In languages like C and C++, compiling and linking multi-file programs is surprisingly time-consuming due in large part to the use of #define to combine files. Many large C++ programs can’t be efficiently built on a single computer, and so sophisticated distributed build systems are used to make build times more reasonable.

    Apparently, the designers of Go got the idea for creating it during a 45 minute wait for a C++ compilation.

  • Lightweight typing. Go is a statically typed language, like C++ or Java. Yet this is done in such a way that it feels closer in spirit to non- statically typed languages, like Python or JavaScript. You can often avoid explicitly dealing with types.

  • Novel use of interfaces. Go is object-oriented, but it does not have classes or inheritance (at least in the sense of C++ or Java). Instead, interfaces and methods are combined to provide most of the same benefits of traditional object-oriented programming.

  • Garbage collection. As in Java or Python, there’s no need to explicitly de-allocate memory. This simplifies code, and goes a long way towards stopping memory leaks and preventing dangling pointers.

  • Closures. Go supports anonymous functions (i.e. lambda functions), which are functions without names. Anonymous functions are allowed to refer to variables outside of themselves, and so you can pass and return functions.

  • Concurrency support. Go uses channels and so-called “go routines” (essentially lightweight processes) to handle most concurrency tasks. These are based on a model of concurrency known as communication sequential processes.

  • No exceptions. The designers of Go believe that exception systems in languages like C++ and Java ultimately lead to convoluted code that treats too many situations as errors (e.g. they believe that a failure to open a file should not be treated as an exception). Instead, Go relies on explicit error codes returned from functions, along with the functions defer, panic, and recover.

  • Pretty good standard tools and library. Out of the box, Go comes with useful tools for things like source code formatting (go fmt) and testing (go test). It also has an extensive standard library with many practical packages. For instance, it is relatively easy to create a simple web-server in Go using just its standard library.

Hello, Go!

Here is the basic “Hello, world!” program in Go:

// hello.go

//
// To run this, type the following at the command-line:
//
//   $ go run hello.go
//

package main

import "fmt"

func main() {
    fmt.Println("Hello from Go!")
}

A few thing to notice:

  • Go code is organized into packages, and a runnable Go program must have one package called main, and within that, it must have one function called main.
  • fmt is a package in the Go standard library that contains basic reading and writing functions such as fmt.Println.
  • Code statements can end with a ;, but don’t need to. In practice, they are rarely used to end statement.
  • All function declarations start with func.
  • Source code comments work as in C/C++, i.e. // to the end of a line is a comment, and everything /* and */ is a comment.
  • Go is a compiled language, and we will be mainly using the go run command to compile and run our programs in one step. Go usually compiles pretty quickly, and so using go run` can make it feel quite interactive.
  • The command go fmt can be used to format a Go program in a standard way, which greatly increases code readability. Many Go editors will automatically call go fmt every time you save your code so that it will stay in a nearly formatted style all the time.

Prime Number Sample Code

Here is the prime number checking code used in some of the lectures:

// primes.go

package main

import (
  "fmt"
)

func isPrime(n int) bool {
  if n < 2 {
    return false
  } else if n == 2 {
    return true
  } else if n%2 == 0 {
    return false
  } else { // n > 2
    trialDiv := 3
    for trialDiv*trialDiv <= n {
      if n%trialDiv == 0 {
        return false
      }
      trialDiv += 2
    } // for
    return true
  } // if
}

func isPrime2(n int) bool {
  switch {
  case n < 2:
    return false
  case n == 2:
    return true
  case n%2 == 0:
    return false
  default: // n > 2
    trialDiv := 3
    for trialDiv*trialDiv <= n {
      if n%trialDiv == 0 {
        return false
      }
      trialDiv += 2
    } // for
    return true
  } // switch
}

// table-driven testing
var prime = []int{2, 3, 5, 7, 11, 13, 17, 31, 37, 101}
var notPrime = []int{-2, -1, 0, 1, 4, 6, 8, 9, 10, 12, 91, 100}

func testIsPrime(isPrime func(int) bool) {
  for _, n := range prime {
    if !isPrime(n) {
      fmt.Printf("testIsPrime: %v is prime!\n", n)
      panic("error!")
    }
  }
  for _, n := range notPrime {
    if isPrime(n) {
      fmt.Printf("testIsPrime: %v is not prime!\n", n)
      panic("error!")
    }
  }
  fmt.Println("all testIsPrime tests passed")
}

func countPrimesLessThan(n int) (count int) {
  if n < 2 {
    return 0
  } else {
    for i := 2; i < n; i++ {
      if isPrime(i) {
        count++
      }
    }
    return count
  }
}

func main() {
  testIsPrime(isPrime)
  testIsPrime(isPrime2)

  // nvals := []int{10, 100, 1000, 10000, 100000}
  // for _, n := range nvals {
  //  fmt.Printf("countPrimesLessThan(%v)=%v\n", n, countPrimesLessThan(n))
  // }
  // for i := 0; i < len(nvals); i++ {
  //  fmt.Printf("countPrimesLessThan(%v)=%v\n", nvals[i], countPrimesLessThan(nvals[i]))
  // }
  // for i := 0; i <= 25; i++ {
  //  fmt.Printf("isPrime2(%v) = %v\n", i, isPrime2(i))
  // }
} // main

Notes on Go

Here are some notes introducing various basic features of Go: