Skip to content

Instantly share code, notes, and snippets.

@panamafrancis
Last active January 13, 2024 15:48
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save panamafrancis/b97d0a52d9fcbd300836842dc2a1706a to your computer and use it in GitHub Desktop.
Save panamafrancis/b97d0a52d9fcbd300836842dc2a1706a to your computer and use it in GitHub Desktop.
Golang Beginner Tutorial

Introduction To Programming in Go

Welcome to this brief tutorial for programming in Go, a language created at Google in 2007 and supported by a large and active open source community.

If you're new to programming, you might have asked yourself before 'What is programming?', now if you've not programmed anything before then don't worry, this tutorial is aimed at you. Programming is far from the mystic dark art that it is portrayed as, programming isn't in itself problem solving, and solving problems that have to do with computers doesn't actually mean you have to be sat down at one. So if problem solving is just thought, then programming is formalizing those thoughts so they can be executed by a machine.

The art of being a programmer is in breaking down messy problems into small simple functions that can be composed into one larger program that solves said problem.

Setting up your environment

I'd recommend starting with http://play.golang.org it's a simplified environment for testing out simple programs and for the rest of this exercise it's all you need.

If you wish, you can use and IDE like IntelliJ or Visual Studio Code, they make it easy to solve large problems and also let you save your work. After you've finished this I'd suggest you download one of those.

Into the shallow end

Your mate Mike sells double glazing and has started a new career as a serial entrepreneur, as his gullible but technically gifted friend you've agreed to implement the MVP for his uber-for-windows idea.

Mike first needs a hand in calculating the amount of glass needed for all the so he knows just how much to buy for each of his as of yet non-existent customers.

The first thing you should start doing is ask questions...

  • How big is each window?
  • How many windows are there?
  • What's the total area?

For the moment just copy the code or click the golang playground link, i'll explain the syntax as we go.

First let's calculate the size of a single window:

package main

import (
	"fmt"
)

func main() {

	// in millimeters
	var windowHeight = 1100
	var windowWidth = 750

	var windowSize = windowHeight * windowWidth

	fmt.Println("Window Size: ", windowSize)
}

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

What's going on here is we create two variables for the length and width, and using these calculate the size, then we print this out to the user.

The stuff at the top is just a formality for creating a new program:

package main

This tells the compiler we're creating a program that can be run by itself, the opposite would be a library package that contains other functions for use by multiple programs.

import (
    "fmt"
)

If we want to use an external package we need to tell the compiler to import it, the 'fmt' package is used to print and format text. We call it's functions such as 'Println' (print line) by calling the '.' dot operator.

func main() {
}

This is the entry point for the program, this is the function that will be called first, we can create other functions and call them anything else but when a go application is executed this one is always the first to be called.

Note the curly brackets... they enclose the contents of block such as a function or an 'if' statement.

Variables in Go

A variable in go can be declared either in a function or outside, the difference is that variables declared outside can be accessed by any other function.

There are also three ways of declaring variables:

var age int = 10
var age = 10
age := 10

Both all have the same result, but the last one is the most simple, but can only be used inside a function.

Functions in Go

A function is the simplest component of any program they take care of a few tasks and ideally aren't very long.

In go we have a few different types of functions but the anatomy of this function is as follows:

func calculateWindowSize(height int, width int) int {
    //...
}

The 'func' keyword indicate its a function (duh)

The 'calculateWindowSize' is the function name, this could be anything without spaces or starting without a number but if the first letter is a capital it has a slightly different meaning, but that relates to exposing functions in library packages.

Everything between the '( )' brackets are what we call the functions parameters. this is a comma separated list of items we need in order to do the work required. The name is proceeded by the type, so for instance in this case both the length and width are of type 'int' which is short for integer, a whole number.

The bit after the parameters but before the first curly bracket '{' is the return type. There can be none or many and they can even be named.

Everything between the curly brackets is the function body, if there's a return type specified then you need to make sure to use the 'return' keyword at the end to return the result.

Back to the MVP...

Seeing as we have more than the size to calculate and we don't want to write any ugly code lets put the size calculation in another function.

package main

import (
	"fmt"
)

func calculateWindowSize(height int, width int) int {
	windowSize := height * width

	return windowSize
}

func main() {

	var windowHeight = 1100
	var windowWidth = 750

	var windowSize = calculateWindowSize(windowHeight, windowWidth)

	fmt.Println("Size: ", windowSize)
}

https://play.golang.org/p/36kG8Yc0Ya

So here we've moved the size calculation out of the main function, we could have placed it below or even in another file in the same directory golang just cares that it's in the same 'main' package.

Now we have the MVP...

But, it's kinda hard to use, it's really just a glorified script and it's not going to be all that great to run. But at least now you can demo it to mikes hapless investors.

Who owns what?

As a piece of software matures more and more functions get added on, eventually you need to start the process of refactoring, which means to change a piece of code without changing its behaviour. It's either cosmetic and complete waste of time or it's the cornerstone of quality software depending on which side of the fence you're on.

As you can see the software is growing but the ease with which it can be read is not. So let's stick all the data and the functions together into a custom type.

type Window struct {
    Height  int
    Width int
}

Now if we wanted to use our new type we'd have to create a new variable with it in it.

windowOne := Window{Height: 1100, Width: 750}

And we could access the members of our window variable like so:

fmt.Println("Height: ", windowOne.Height)
// Height: 1100

Now because we're neat and tidy we want our functions to be functions of our new type 'Window':

func (p *Window) CalculateWindowSize() int {
    windowSize := p.Height * p.Width
    
    return windowSize
}

This is just like any other function but the '(p *Window)' bit means that this function is a 'pointer receiver'. Don't worry what that really means for now, just know that if you wanted to access the members of the Window variable that this function was called on you could access through using the dot operator on the 'p'. Think of the 'p' as a local variable that points to 'windowOne'.

// create a window variable
windowOne := Window{Height: 1100, Width: 750}

// call its calculate function
windowOne.CalculateWindowSize()

So to sum it up, all your data (state) can be held in an instance of your custom Window type, and all the functions about window's can be made only work with your Window type.

This is what it looks like now:

package main

import (
	"fmt"
)

type Window struct {
	Height int
	Width  int
}

func (p *Window) CalculateWindowSize() int {
	windowSize := p.Height * p.Width

	return windowSize
}

func main() {

	// create our windows
	windowOne := Window{Height: 1100, Width: 750}
	windowTwo := Window{Height: 1100, Width: 1500}

	total := windowOne.CalculateWindowSize() + windowTwo.CalculateWindowSize()

	fmt.Println("total: ", total)
}

https://play.golang.org/p/3QR95ab3Au

If then else

Mike likes what you've done so far, but he needs it to make decisions about how much work installing the double glazing is going be. So lets add some "AI" to our prototype.

func analyzeTotalArea(area int) string {
    if area < 0 {
        return "What's going on ere then?"
    }

    if area == 0 {
        return "Nothing to do here mate!"
    }

    if area < 100000 {
        return "easy it'll take me a day"
    }

    if area < 1000000 {
        return "ooh dunno, could be 3 days work"
    }

    if area >= 1000000 && area < 5000000 {
        return "oh that's at least 2 weeks work"
    }

    // default
    return "nah mate, ain't got time for that"
}

So an if statement executes a block of code if the expression between the 'if' and the first '{' evaluates to be true.

if true {
    fmt.Println("This will always execute")
}

if false {
    fmt.Println("This will never execute")
}

The 'expression' could be a number of things as long as it will end up being true or false.

You can also chain expressions using boolean operators

if (a < b) || (a < c) {
    fmt.Println("this will only execute if 'a' is less than 'b' OR 'a' is less than 'c'")
}

if (a < b) && (a < c) {
    fmt.Println("this will only execute if 'a' is less than 'b' AND 'a' is less than 'c'")
}

Here's a link to the finished program:

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

Create your own "AI" and add it to your program, use what you've learnt to extend the program or come up with your own ideas and implement them.

Fin

Mike is "pumped" about your MVP and years later after you left he sells it to yahoo as part of an acquihire exit deal for a reported $1 billion, however you neglected to wait until your options vested and you totally missed out.

Why Go?

Go is what you'd call a 'C' based language, it's strongly typed and compiled, which means that any program you write will be checked for basic correctness before it can be run, and absurd occurrences such as a number of apples being accidentally compared to a number of pears will be caught by the compiler.

Programming Language research is an abstract endeavor undertook by serious theoretical computer scientists who in their wisdom like to make things complicated for everyone else. Take C++ for example, which like Go is also based off of 'C', the reasoning behind choosing to solve a problem in C or C++ would be speed, as these languages are very close to the machine, but this advantage is also a disadvantage as now the programmer is responsible for everything and with all this extra complexity the likelyhood of mistakes increases.

Go was designed with the day-to-day experiences of programmers in mind, it was never designed to be the next breakthrough in programming language research. Go doesn't implement quite a few features that make languages like C++, Java and Python so powerful. The three main reasons one should program in Go are: readability, speed and maintainability.

When you program everyday you will probably read a few thousand lines of someone elses code, with other languages code formatting is left as a political exercise for the team. You have things such as 'Style Guides' which weigh in on religious matters such as how you format braces '{ }' and how comments should look. The problem being is that everyone has their own 'right' way of doing things. Go imposes it's own style, that everyone must use, it serves to settle the argument, and enable programmers to get on with doing what they're really supposed to be doing.

When I say speed, Go will never be faster than C, I'm referring to the time it takes to compile the source of a program and the number of hours it takes to develop it. A large complex program in C++ will take anywhere from around 10 minutes and over an hour to compile, this stop-start kind of development can be debilitating to all but the most determined of programmers. Whereas in Go it's reasonable to expect a large package to compile in a matter of seconds and when you run it, it's performance will be perhaps only 30% slower than C. Which for the majority of projects is a good trade off given that it is pretty easy to develop a robust solution to a problem in Go in a relatively short amount of time.

Maintainability could be summarized as the amount of risk legacy code in your organization carries. The more risk you carry the harder it is to maneuver a project around the ever changing landscape of customer requirements. Legacy code is just code that is untested or improperly tested, in that when a change is made to an old piece of code, you can't verify that the old behaviour still holds. You can just as easily write bad legacy code in Go as you can in any other language but the ease with which Go code can be tested removes the age old excuse of testing being far too hard.

To sum it up... why should one program in Go?

  1. It's easy to understand what's going on - once you've got used to the syntax it's trivial to read anyone elses code

  2. It's productive - you can write pretty fast code in a short amount of time.

  3. It's easier to get it right - most bugs occur in code that can't be tested or easily understood by others.

Going Further...

If you want to learn more about Go, please checkout the official Go tutorial and most importantly start building little programs to demonstrate to yourself that you understand each area of the language. Good luck!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment