First Look At Go

Oct 26, 2018 18:00 · 1381 words · 7 minute read go golang

My goal since 16.10 was to learn about Go.
It was just 10 days, so I didn’t really have chance to dive deep into Golang. The point was to learn some syntax, specification, purpose of this language and whether I should learn more about it.

I’ll try to summarize information I found most interesting.

Go is described as fast, statically typed, compiled language that feels like a dynamically typed, interpreted language.

Dependencies

Go was created by Robert Griesemer, Rob Pike, Ken Thompson at Google. One of it’s purposes was to reduce long compilation times. Working with C, they saw that dependencies could be improved. In larger applications it was difficult to determine which one are no longer required. At a scale this overhead stacked up into a problem.

That’s why in Go, unused imports are compile-time errors. You never need to worry about unused dependencies.
Another thing is how they are handled by compiler:

  • Package A import package B
  • Package B import package C
  • Package A does not import package C

Last thing you should keep in mind about dependencies is that Go doesn’t support cyclic imports.
The reason is.. it’s just simpler without them. This motto helped creators to design Go in every aspect.

Syntax

The idea was to create easy and clean syntax.

Variable declaration

The first thing that stands out is variable declaration.

x := "Foo"

This is just syntactic sugar that stands for declaration and initialization.
It’s equivalent to

var x = "Foo"

Using either option there is no need to declare type. It’s determined based on assigned value. If you want to declare variable without value you have to specify type

var x string

It’s important to notice the difference between C and Go when it comes to declarations. In C, you first specify type and then variable name. In Go it’s the other way around.

Example:

func main(int, []string) int {} // Go
int main(int, char *[])         // C

It makes difference when there are nested functions.

Go was designed this way to make reading easier. It is designed in left to right style, whereas C is designed with spiral rule.

Public / Private

Another small quirk is how you specify visibility of an identifier. If you want to make function accessible from other packages you name it with upper case initial letter.

func Sqrt(x float64) float64 {...}
func pow(x int) int {...}

In this example Sqrt is public and pow is private. This works for everything, variables, functions, methods and so on.

Loops

Or more precisely one loop, for. Next place where simplicity took over and Go was created with one type of loop. It looks quite like in C.

for i := 0; i < 100; i++ {
    // Default loop
}

for i < 100 {
    // Without declaration
}

for {
    // Infinite loop
}

for i, v := range some_array {
    // Loop with enumeration
}

Errors

There are no exceptions in Go. There are many opinions whether it is a good thing or a bad thing. I would have to work with Go for some time to make up my mind about this. Personally I think it might be a good way to eliminate abuse of exceptions and push developers to make better designs.

An example how it looks like:

file, err := os.Open("file")
if err != nil {
    log.Fatal(err)
}

OOP

Composition over inheritance

Go has quite interesting way of dealing with Object Oriented Programming. There is no inheritance, overloading, type hierarchy.

Interfaces and polymorphism

Interfaces is Go are specified implicitly, without implements keyword or other equivalent. You can define interface geometry with area() method

type geometry interface {
    area() float64
}

then create type rectangle with area() that returns the same type

type rectangle struct {
    width, height float64
}

func (r rectangle) area() float64 {
    return r.width * r.height
}

then rectangle implicitly implements geometry so you can do:

func calculate_area(g geometry) float64 {
    return g.area()
}

This is one of the most interesting feature of Golang for me right now. It makes you think a bit different than when programming with language that has inheritance.

Encapsulation

It’s provided by capitalization of variables and methods. There is no need for protected since there is no inheritance.

Concurrency

The most interesting topic for me. I like the idea od writing concurrent programs. In Python it is quite overwhelming. There is an interesting blog post written by Armin Ronacher about AsyncIO.

In Go it seems more elegant, easier to understand and more efficient.

Gorutines

To run functions concurrently we need to have multiple stacks and a way to jump between them. To do it in Go, we use Gorutines which are like cheep threads. One thread can handle thousands of Gorutines. To invoke Gorutine we use keyword go.

Let’s say we have some input stream of integers and we want to print them or make some calculations.

// Version: 1
func fetch_numbers() []int {
    numbers := make([]int, 10, 10)
    for i := 0; i < 10; i++ {
        time.Sleep(1 * time.Seconds) // 1s blocking IO
        numbers[i] = rand.Intn(1000)
    }
    return numbers
}

func main() {
    numbers := fetch_numbers()
    for _, v := range numbers {
        fmt.Println(v) // Print or Calculation
    }
}

Version 1 shows the most primitive way It fetches all the numbers from blocking IO, puts it into slice and then returns for further operations.

Running fetch_number() in Gorutine doesn’t improve anything. To make use out of Gorutines we need a way to communicate between them.

Channels

Gorutines can communicate using channels or mutex. For this example I’ll use channel.

Chanel is like a pipe, you can read from it or send elements through it.

// Version: 2
func fetch_numbers(channel c) {      // Takes channel
    for i := 0; i < 10; i++ {
        time.Sleep(1 * time.Seconds) // 1s blocking IO
        c <- rand.Intn(1000)         // Feeds channel with number
    }
    close(c)                         // Closes channel
}

func main() {
    c := make(chan int)    // Creates channel of integers
    go fetch_numbers(c)    // Runs fetch_numbers in new gorutine
                           // passing channel
    for _, v := range c {  // Iterates over received numbers from channel
        fmt.Println(v)     // Print or Calculation
    }
}

Version 2 is better because allows Go to switch Gorutines when fetch_numbers() is blocking. It can go back to iteration (receiver) in main() function and continue printing. Since we have receiver that waits for next element from channel, it’s important to close it.

Here is a version with some improvements.

// Version 3
func fetch_numbers() chan int {  // returns a channel
    c := make(chan int)          // creates channel inside a func
    go func() {                  // runs gorutine inside a func
        for i := 0; i < 10; i++ {
            time.Sleep(1 * time.Second)
            c <- rand.Intn(1000)
        }
        close(c)
    }()
    return c
}

func main() {
    c := fetch_numbers()
    for v := range c {
        fmt.Println(v)
    }
}

Here in main function we don’t need to create channel. It can be returned by fetch_numbers() function which takes care of creating Gorutine.

It looks much cleaner.


Overall Go seems like a well designed, low level language that is good to have in your skill set. I’ll try to play with it when writing my personal projects.

Most of the time I wouldn’t choose it over Python. Especially at work, where we create new products that needs to be delivered quickly.

It may come necessary to build very fast service, or create binary that doesn’t weight much and can be uploaded on some kind of device. Then, I’ll probably consider using Go.

At the moment I don’t see Go as a replacement to C++ or Java, in which big teams will create their huge infrastructure. It needs to mature.. or maybe we need to mature? Maybe developers need to become more disciplined, less chaotic, create better architecture and principles?


Next task for me will be to create blog on my own server. I wanted to do it from the beginning but I knew it would take me to much time and I wanted to start as fast as possible.

I won’t focus on making it the best I can, but mostly to learn new things and to deliver on time. I can improve it later on.

Deadline 7.11.2018 (12 days)

tweet Share