Fixing: File is not `goimports`-ed (goimports)

The other day, I was linting my Go code with golangci-lint when I got this error: File is not `goimports`-ed (goimports) I examined the file and found nothing amiss, but the linter insisted something was wrong. Eventually, I realized I had used spaces to indent the file rather than tabs. Changing the indent character to tabs fixed it. I then ran into the same error on a second file. This time, I had a comment which was wrong. There were two spaces before the first word in the comment (see below). ...

2022-10-05

Linux load average

This post documents the high level concepts I need to remember about *nix Load Average. Load average can been seen in the output of uptime and top: From uptime: $ uptime 14:40 up 21 days, 21:48, 5 users, load averages: 2.63 2.75 3.45 From top: root@blog-server-2:~# top -n 1 | head -2 top - 15:28:37 up 10 days, 12:41, 1 user, load average: 0.04, 0.03, 0.00 Tasks: 95 total, 1 running, 94 sleeping, 0 stopped, 0 zombie root@blog-server-2:~# There are 3 decimal numbers shown. They present the load average over three different time scales: ...

2022-09-16

Using entr to get immediate test feedback

Old and new workflows My dev workflow used to look like this: Write some code and save it Change to my Terminal window Up-arrow to find my command to run unit tests (typically make test) Press Enter Wait for tests to run and examine test output This worked pretty well, but was a lot of manual steps when you figure that I did this dozens (hundreds?) of times per day. That’s a lot of energy spent doing the same thing over and over again. ...

2022-09-07

Go and pass by value

Go normally uses pass-by-value for function calls. When you pass a variable into a function or method, Go will (under the hood) create a new variable, copy the old variable’s value into it, and then pass the new variable (not the original variable) into the function or method. Non-pointer values These types behave as described above and are sometimes called non-pointer values: Strings Ints Floats Booleans Arrays Structs Here’s an example of how these work. Note that the variables myString and si do not point to the same memory address: ...

2022-09-01

Constants in Go

Constants syntax I always have a hard time remembering the syntax of declaring constants in Go. So here’s some reminders to myself. It is possible to declare constants one per line like you’d expect. Note that the there are typed and untyped constants as shown here below. package main import "fmt" const vermKnid string = "scram" const fox = "foxes likes loxes" func main() { fmt.Println(vermKnid) fmt.Println(fox) } But you can also use a constant declaration group, which does the same thing but is easier to read if you’re declaring a bunch of constants. ...

2022-08-16

Open a file or folder from the macOS CLI

You can open the a file from the macOS CLI with the command open. To open a file with the default app, use: open $FILENAME To open the current directory, use: open . To open a specific directory, use open $DIRECTORY_NAME To open a URL, use: open $URL Thanks to this post for teaching me this.

2022-08-09

Python type hints

After working with Go for a while, one of the biggest drawbacks to Python for me is that the types of parameters to functions and methods are completely opaque without good documentation. For example, let’s take this silly function right here: def my_func(thing1, thing2): print(thing1 + thing2) This function will happily accept strings or ints (or floats, for that matter) as input and depending on the type, will do completely separate things! ...

2022-08-05

Mutexes and concurrent access in Go

Golang has mutexes (short for mutually exclusion) to manage concurrent access to a shared object via multiple goroutines. Here’s an example (taken from the excellent Go by Example: Mutexes). package main import ( "fmt" "sync" ) type Counter struct { mu sync.Mutex count map[string]int } func (c *Counter) Inc(name string) { c.mu.Lock() defer c.mu.Unlock() c.count[name]++ } func main() { cnt := Counter{count: map[string]int{"james": 0, "spartacus": 0}} var wg sync.WaitGroup increment := func(name string, n int) { for i := 0 ; i < n ; i++ { cnt.Inc(name) } wg.Done() } wg.Add(3) go increment("james", 100000) go increment("james", 100000) go increment("spartacus", 10000) wg.Wait() fmt.Println(cnt) } This code defines a simple Counter struct with a single method named Inc to increment the count map. Inc is responsible for managing the locking and unlocking of the mutex with c.mu.Lock() to lock it and defer c.mu.Unlock() to unlock it as the method is returning. ...

2022-07-22

Go routines and WaitGroups

Working with Goroutines is simple. You simply pre-prend your function call with go and you’re off and running. Here’s an example which calls the expensive() function five times before exiting. package main import ( "fmt" "time" ) func expensive(id int) { fmt.Printf("Worker %d starting\n", id) time.Sleep(time.Second) // Oh, so expensive! fmt.Printf("Worker %d ending\n", id) } func main() { for i := 0; i < 5; i++ { go expensive(i) } fmt.Println("Exiting!") } But it has a problem… running the program shows output like this: ...

2022-07-18

Closures in Go

In Go, it’s pretty easy to write an anonymous function. package main import "fmt" func main() { func(s string){ fmt.Println("s is:", s) }("hello world") } Running the above code produces: $ go run main.go s is: hello world You can even assign the anonymous function to a variable like so: package main import "fmt" func main() { myFunc := func(s string){ fmt.Println("s is:", s) } myFunc("hello again, world!") } Now we get this output from the above code: ...

2022-07-13