40. Other languages - Go

Section author: Sophia Mulholland <smulholland505gmail.com>

https://golang.org/dl/

40.1. Hello World in Go

Go is similar in syntax to C, yet simpler in many ways. C definitely has more structure and syntax rules that Go does not have, which makes Go simpler and easier to learn. Writing a short program in Go looks like a combination of C and Python. Syntax like this allows you to get comfortable with the language quickly and move on to working on harder things.

First, we will get comfortable with running a Go program. In an editor such as emacs, open up a new file called “hello.go”.

The first program to write in Go is a simple “Hello World” which is shown below:

Listing 40.1.1 ‘hello.go’
package main

import "fmt"

func main() {
	fmt.Println("Hello, World!")
}

Notice: Every Go program should have a ‘package main’ defined at the top of the code, with the majority of programs using that package. The line ‘import “fmt”’ is basically like writing ‘#include <stdio.h>’ in C. It imports the package ‘fmt’ that allows you to print things. The last thing to notice is that Go does not have any semicolons.

Compile and run this code with

$ go build hello.go
$ ./hello
$ Hello, World!

OR optionally the Go compiler supports a ‘go run’ command which will just compile and run it in the same step:

$ go run hello.go
$ Hello, World!

40.2. Writing a Go program with command line arguments

Please see the chapter on differential equations for the context to this program. It is meant to graph the output of using the euler method to solve equations and compare it with the exact answer.

Let’s write another simple program to get familiar with the syntax of the language called ‘euler-method.go’.

Listing 40.2.1 ‘euler-method.go’ solves differential equation using Euler’s method
package main

import (
	"fmt"
	"math"
	"os"
	"strconv"
)

func differential (x float64, y float64) float64 {
	return (2*x)-2
}

func solution (sx float64, sy float64) float64 {
	return math.Pow(sx, 2) - (2*sx)
}

func main() {
	var n_steps int = 1
	if len(os.Args) == 1 {
		n_steps = 1000
	} else if len(os.Args) == 2 {
		n_steps, _ = strconv.Atoi(os.Args[1])
	} else {
		fmt.Println("error usage is %s [n_steps]\n", os.Args[0])
		os.Exit(1)
	}
	var set_y float64 = 0
	// define initial x, initial y, step size, and # of steps
	var interval float64 = 10
	var dt float64 = interval/float64(n_steps)
	var xi float64 = 0
	var yi float64 = set_y
	for j := 0; j < int(n_steps); j++ {
		//find exact solution to compare approximation with	
		var exact float64 = solution(xi, yi)
		//solve differential with (xi, yi)
		var m float64 = differential(xi, yi)
		//use tangent line to approximate next y value from previous y value
		var new_y float64 = yi + dt * m
		//increase x by step size
		var new_x float64 = xi + dt
		xi = new_x
		yi = new_y
		fmt.Println(xi, yi, exact)
	}
}

As we can see in the code, Go has a different way of declaring variables and handling command line arguments. In Go, handling command line arguments is simpler as you can import the package “os” and not have to deal with the argc and *argv[] or understanding pointers yet. By just importing “os” you can deal with command line inputs as an array and can check the length of that array rather than dealing with the number argc.

To deal with converting this array of string inputs into integers, we can import “strconv” which converts the strings into ints. However, this strconv function converts it into a number plus a <nil> at the end so avoid this compiler error when we try to declare n_steps, we can declare the variable n_steps along with the blank identifier ‘_’ to avoid having to declare all the return values (<nil>).

$ go build euler-method.go
$ ./euler-method 1000 > euler-method.dat

gnuplot> plot "euler-method.dat" using 1:2 with lines
gnuplot> replot "euler-method.dat" using 1:3 with lines
../_images/euler-method1.svg

40.3. Goroutines and channels

A cool thing that Go does well is run goroutines. These are functions that can run concurrently with other functions. Concurrency means that the functions are running at the same time rather than waiting for one to finish and then the next program starts. Basically it is used when we need to handle multiple things at the same time. Go makes these kinds of programs simple to write and easier to understand because concurrency is generally difficult to understand in computing. Also, these goroutines do not take up a lot of memory and so they are very fast.

Threads are most commonly used to run things concurrently. All processes have at least one main thread that executes a task. Threads take a fixed amount of memory in the stack to create and when many threads are used, switching between them gets messy.

However, using goroutines is like a step above threads. The programmer deals with them while the computer does not even know goroutines exist. They take little memory to create, 2kB, which means you can have millions of them running on one CPU. They can also shift in size, to accommodate the stack. Go also deals with the switching of goroutines for the programmer, pausing and running. This is an advantage to goroutines because the programmer does not need to say ahead of time when to stop and start the threads.

Let’s see how to implement these goroutines.

Listing 40.3.1 ‘goroutines-hello.go’
package main

import (
	"fmt"
)

func hello() {
	fmt.Println("Hello world")
}

func main() {
	go hello()
	fmt.Println("main function")
}

We have a function called hello, which looks just like a normal declaration of a function and what makes it a goroutine is putting ‘go’ in front of the function call. So let’s run it.

$ go build goroutines-hello.go
$ ./goroutines-hello

The output is

$ main function

That’s weird. We called the function hello(), and it did not print “Hello world” like we wanted. That is because by making it a goroutine, the compiler did NOT wait for it to finish its task of printing “Hello world” and it went on with the rest of the main function. When the main function finishes, the program is over no matter what, and hello() did not have enough time to finish.

Let’s write a program that deals with more goroutines and introduce a way to make sure the program waits for the goroutines to finish.

Listing 40.3.2 ‘goroutines.go’
package main

import (
	"fmt"
	"time"
)

func hello() {
	fmt.Println("Hello World")
}

func listnumbers() {
	for i := 0; i < 10; i++ {
		fmt.Println(i)
	}
}

func squareroot(x int) {
	if x == 2 {
		fmt.Println("Oh no, a 2")
	} else {
		fmt.Println("good choice")
	}
}

func main() {
	//run all three goroutines
	go hello()
	go listnumbers()
	go squareroot(3)
	//wait for the goroutines to finish
	time.Sleep(2 * time.Second)
	fmt.Println("All finished")
}
$ go build goroutines.go && ./goroutines

If we ran it in the exact same way, the goroutines would not have time to finish so let’s help it do everything it’s supposed to. Notice how we added a line for the computer to “sleep” as the goroutines finished and it we run it, it prints out everything we wanted it to. If you run it multiple times you’ll find the order of the outputs changes, as depending on which goroutine is running on which core, it will finish first.

So these goroutines are cleaner and easier to implement than in C where you need pointers and lots of lines of code where it can get messy quickly.

So, in these programs we have no way of knowing when the goroutines end. Therefore, we have to make the program wait for two seconds to be sure it does. This is where channels come in.

A way goroutines can communicate with each other is through channels. Channels can send and receive data of one type. If a goroutine sends something to a channel, it automatically waits until a goroutine can receive that data. It will be blocked until the goroutine requests that data and vice versa.

Listing 40.3.3 ‘channels-example.go’
//channels-example.go
package main

import (
	"fmt"
	"time"
)


func pinger (c chan string) {
	for i := 0; ; i++ {
		//send "ping" to the channel
		c <- "ping"
	}
}
//channel gets "ping" and waits until printer() is ready to receive "ping"

func printer (c chan string) {
	for {
		//receive "ping" from channel and assign it to msg
		msg := <- c
		fmt.Println(msg)
		time.Sleep(time.Second)
	}
}

func main() {
	//makes a channel called c
	//strings are passed on it
	var c chan string = make(chan string)

	go pinger(c)
	go printer(c)

	//input receives data from the channel c
	//input := <- c
	//will print ping
	//fmt.Println(input)

	var input string
	//scans the input from c channel and prints it out
	//does not stop until you exit the program running
	fmt.Scanln(&input)
}
$ go build channels-example.go && ./channels-example
$ ping

So this program created the channel c, and ran two goroutines (pinger() and printer()). Pinger() sends “ping” to the channel and Printer() prints out the message it received from the channel. In the main function, we also show that the variable ‘input’ can receive data from the channel too and print it out.

So goroutines allow functions to be run concurrently and channels are how these goroutines communicate. Go offers an easy way to implement them and it is

40.4. More complicated Go program

Lastly, let’s take a look at a more complicated program from the differential equations chapter rewritten in Go.

Listing 40.4.1 ‘damped-spring-with-friction-plain.go’
package main

import (
	"fmt"
	"math"
)

// acceleration due to gravity
var g = 9.8
// physics values for spring equation
var k = 3.0
var m = 2.0
// physics for air friction
var air_friction = 3
// physics for frictional damping force
var damp = 0.5
var F0 = 10.0			// constant driving force


func Acceleration (t float64, v float64, x float64) float64  {
	return ((-k*x) - (damp*v) + (F0*math.Cos(8.0*t))) / m
}

func Harmonic_exact (t float64, st float64, sx float64, sv float64) float64 {
	//return math.Cos(t)
	return sx*math.Cos(math.Sqrt(k/m - damp*damp/(4*m*m))*t)*math.Exp((-damp/(2*m)) * t)
}

func Falling_body_exact (t float64, st float64, sx float64, sv float64) float64 {
	return sx + sv*t - 0.5*g*math.Pow(t,2)
}

func Damped_spring () {
	//set variables for falling_body_exact()
	var set_t float64 = 0
	var set_x float64 = 5   /* try 10 for falling body, 5 for harmonic */
	var set_v float64 = 0	/* try 10 for falling body, 0 for harmonic */
	//initial variables
	var interval float64 = 0.001 // how many seconds
	var n_steps float64 = 1000000
	var dt = interval/n_steps // size of steps
	var ti = set_t
	var xi = set_x
	var vi = set_v
	for j := 0; j < int(n_steps); j++ {
		var exact float64 = Harmonic_exact(ti, set_t, set_x, set_v)
		var acc float64 = Acceleration(ti, vi, xi)
		var new_v = vi + (dt * acc)
		var new_x = xi + (dt * vi)
		// increase t by dt
		var new_t = ti + dt
		
		vi = new_v
		xi = new_x
		ti = new_t
		fmt.Println(ti, vi, xi, exact);
	}
}

func main () {
	Damped_spring();
}

So this program outputs a solution to a differential equation solved by Euler’s method. The length is pretty much the same as the C program however you can see there are no double types in Go. Instead, it offers the equivalent float64 type which indicates it requires 64 bits in memory.

$ go build damped-spring-with-friction-plain.go
$ ./damped-spring-with-friction-tut > damped-spring.dat
gnuplot> plot “damped-spring.dat” using 1:3 with lines
gnuplot> replot “damped-spring.dat” using 1:4 with line

This output is the same as the chapter on differential equations, go to that chapter for an explanation on what it means. That chapter has the same program as this damped spring one, except it is written in C.

SOURCES

*https://golang.org/cmd/go/*

*https://github.com/vladimirvivien/go-cshared-examples*

*https://golangbot.com/hello-world/*

*https://medium.com/rungo/achieving-concurrency-in-go-3f84cbf870ca*

*https://www.sohamkamani.com/blog/2017/08/24/golang-channels-explained/*