This post is part of my tech learning series, where I take a few hours each week to try out a piece of technology that I'd like to learn.
This week I decided to try something completely new and spend some time with Go.
Go is a programming language that has seen some success in system programming. My goal with learning Go is to replace some shell scripts and services I run.
This tech learning was a bit harder though, since Go is an entire programming language and a type of language I don't have much experience with.
Documentation
The official Go website has a lot of official documentation. The Tour of Go and How to write Go code sections were useful for me to get started. Other sites like Stackoverflow where useful for specific problems.
Todo list application with Go
Like other weeks, I wanted to build a todo list application with Go. I've done enough of them that I know the scope of the application, so I can focus on learning how Go works.
This time the application was going to be purely command line based. I know Go can be used for web applications but I wanted to keep things simple in my limited time.
Features
That meant that the feature-set would stay small. But even then, I didn't have enough time to complete them all.
-
List all open todo items
-
Add a new todo item
-
Complete a todo item
-
Delete a todo item
-
Save todo items to disk for persistence
Implementation
Being a command line application, my todo program would be using standard input and output for most of its work. I also figured I'd persist the todo items into a file, but I was open to the format of the file (e.g. serialized, json, text).
main(), command parsing
The first part was to accept commands from the command line. I've always like the svn and git style of subcommands over flags so I designed this application the same way. It would be called like:
todo COMMAND OPTIONS todo list todo add "The todo item text" todo complete N todo remove N
For this I found the os
package with the os.Args
function to work great. I had to do a bit of input handling to check the options but a multi-step if
statement worked for that (which I plan to change to a switch
later).
Once the correct command was found, the flow would be passed off to the instance of the todolist.
todolist and todo structures
The main object used is a todolist structure which holds an array of todos. These todos are composed of a name and a done flag.
My intention was to have a single todolist that would serve as the behavior for managing the list.
todolist.list()
The list()
function would take the existing todolist and print out every todo item to standard output. Nothing too complex here, just a for loop and fmt.Println
.
todolist.add()
The add()
function would take a string, from the command line, and create a new todo item on the list with it.
The interesting thing here was how Go treats arrays and slices. Coming from a Ruby background, it felt odd. What would have been a simple append operation, took a bit of research to figure out. Even though I ended up using the append
function, I couldn't tell you if it was creating a new array, creating a slice, or creating a slice and converting it into an array.
The final code works and is simple to read, but I'd like to dig into what's going on to better understand it later.
todolist.save() and todolist.load()
Before I was done I wanted to work on the persistence. What good is a todo list that isn't saved?
(Actually, that might be a good idea if you're overworked...)
I intending to have a function called save()
that would take the current todo items and save them to a file, along with a corresponding load()
. Ideally they'd be in a plain text or human readable format but I was open to any format really.
The problem I ran into was saving the todo items to disk. I needed a byte array to save to the file, but each todo item was using a string. Since there could be zero to a near-infinite number of todo items, I had to be dynamic in how I got each item. I tried a few different conversion methods but I couldn't get them to completely work.
I'd like to blame my lack of a C background for this.
If I had a bit more time, I'm sure I could have figured it out.
Summary
Go was a different enough language from regular work so it stayed interesting. I was frustrated by the type system but I think that's because I didn't spend enough time with it.
The ability to compile to a static binary really appeals to me. That feature alone would make it worth porting some Ruby scripts I have to Go (e.g. text transformation, basic network I/O).
Overall, I'm still interested in Go. I'm not ready to port anything over to it or to take on a project that uses Go heavily, but I'm interested enough to learn more about it.
Work with me
Are you considering hiring me to help with your Shopify store or custom Shopify app?
Code
package main
import ( "fmt" "os" )
type todolist struct { todos []todo }
type todo struct { name string done bool }
// 2. Add new func (l *todolist) add(name string) bool { newItem := todo{name: name, done: false} l.todos = append(l.todos, newItem) return true }
// 1. List open todos func (l *todolist) list() bool { fmt.Println("Your todo list is:") for i := 0; i < len(l.todos); i++ { fmt.Println("- ", l.todos[i].name) } return true }
// 3. Complete // 4. Remove
// command data func main() { var command, data string;
if len(os.Args) > 1 {
command = os.Args\[1\]
}
if len(os.Args) > 2 {
data = os.Args\[2\]
}
l := new(todolist)
if command == "list" {
// No-op, always print list
} else if command == "add" {
l.add(data)
} else if command == "complete" {
} else if command == "remove" {
} else {
fmt.Println("Unknown command:", command)
os.Exit(1)
}
// Always print
l.list()
}