So I have been doing a bit of coding in golang recently and I figured I would share some of my observations.
The Standard Library is Very Robust
I have found so far that everything I have needed to do so far can be done at least pretty well with the standard library. That is not to say it is perfect, but there has been nothing that I could not do. Need to call an API? Just import net/http. Want to convert something to json? Just import encoding/json. Go's standard library has you covered for all of your basic needs including: IO, file system, encryption, etc. So far the only thing I haven't really been a fan of is the standard logger. For that I replaced it with zap logger.
Go's Dependency Management Could Be Better
So far I have exclusively made use of go modules as a feature in Go 1.11 which released in August of 2018. This allows each project to live in a separate directory anywhere in the file system and have version controlled dependencies. From my understanding this is the first official way of having project specific dependencies. So far though I have found some things just do not work using go modules. For instance I was unable to use Revel using go modules, which I had planned to explore as the 2nd entry in my framework impressions series. Compared to npm and composer, the two package managers I am familiar with, Go needs some time to get everything standardized and to get all of the libraries on board.
Go Is Versatile
Specifically there are three unique features of go which offer the developer a ton of flexibility: methods on any data type, multiple return values for functions, and interfaces.
Methods on Any Data Type
So in go any data type can have methods. For instance lets say I want a int that can double itself.
type Doubler int
func (d *Doubler) Double() {
(*d) = (*d)*2
}
func main() {
var num Doubler
num = 3
num.Double()
fmt.Println(num) //prints 6!
}
This can be pretty handy, though be careful not to abuse it. I can easily see this feature abused into some strange spaghetti.
Multiple Return Values
So in go functions can return multiple values of any variance of data type. This allows for some very nice possibilities that require workarounds to achieve in other languages. For instance say you want to compare two people to check if they are the same:
type Person struct {
Name string
Age int
}
//isSamePerson takes two Person structs and returns whether they are equal
//It also returns a list of differences
func isSamePerson(firstPerson Person, secondPerson Person) (bool, []string) {
differences := []string{}
isSame := true
if firstPerson.Name!= secondPerson.Name{
differences = append(differences, "Names do not match")
isSame = false
}
if firstPerson.Age != secondPerson.Age {
differences = append(differences, "Ages do not match")
isSame = false
}
return isSame, differences
}
Here we have a function that checks if two people are the same, and we can get the boolean value that we need, but we also can see what is different.
Interfaces
Interfaces in Go work quite a bit different than other languages. Interfaces represent a sort of contract between two parts of your code defining the behavior of functions. In most object oriented languages an interface is an explicit declaration on a class stating that it must have said functions with the defined requirements in an interface. Go takes a different approach with its interfaces by making them implicitly defined. Any data type that has methods matching the signature of the interface is acceptable to use. This is good and bad in my opinion. Combined with Golang's capability of having methods on any data type this allows for very flexible usage. This means any data type can satisfy the interface if the methods match. Additionally a data type can potentially satisfy multiple interfaces without conflict. I have found while coding is that it is harder to make sure you are implementing an interface. This is largely due to the implicit nature of Go's interfaces. Example time!
type Doubleable interface {
Double()
}
type DoubleInt int
func (d *DoubleInt) Double() {
(*d) = (*d) * 2
}
type DoublePeople struct {
NumPeople int
}
func (d *DoublePeople ) Double() {
d.NumPeople *= 2
}
func main() {
var numInt DoubleInt = 1
people:= DoublePeople{NumPeople: 4}
doDouble(&numInt)
doDouble(&people)
fmt.Println(numInt)//prints 2
fmt.Println(people.NumPeople)//prints 8
}
//doDouble takes a doubler and doubles it
func doDouble(doubler Doubleable) {
doubler.Double()
}
Both an int type and a struct type can both be doubled and can both be passed to the doDouble function since they both implicitly satisfy the Doubleable interface.
Go Has Some Nice Tooling
Unlike other languages I have worked with Go has some nice built in tools. Unit testing, benchmarking, and profiling are all included without needing to install any 3rd party utilities. Additionally debugging also works from the start. This means getting started to writing go code is quite straight forward, at least when using go modules. Go vet can check your code for common problems and go fmt formats your code to the Go standard. Overall compared to PHP and JS at least I needed to install far less things just to get started with writing and testing code. This doesn't mean all of the tools are perfect for everyone, for instance you may want to extend the testing suite with an assertion library or a BDD framework. Regardless Go gives you the tools you need to write good well tested code.
Go is a neat language and so far I have found it to be a pretty simple language to use. There is very little syntax, and a fairly small amount of concepts you need to know to be effective.
Top comments (10)
I think sessions are missing in the stdlib because Go is mostly geared towards servers and API servers than "web apps".
To be even fairer, a lot of languages do not have "sessions" builtin in the standard library, you can build that on top of cookies, which Go supports.
Coming from doing a lot of Python, the strict typing was fabulous to use. That and "go vet" caught so many errors. I found I could write quite complex "script" programs without any unit tests and they would work! (mostly)
Another thing from Python is that the concurrency is much more a part of the core language than in Python. In Python, to do several things at once I'd have to set up a queue for interprocess communications and import more libraries for threading. In Go, all that stuff just happens!
The dependency thing is currently "in progress" to be fixed, I've just converted a project over to having a "go.mod" file which is now working nicely for go 1.11 and this does seem slightly better.
That is fair about the docs. Yeah they are not great, I have had to use some outside resources to clarify some things.
I haven't yet worked with Go in a web context yet, except as a quick performance test with a single endpoint that called a raw sql statement with no auth. So maybe I will run into the templating and sessions at that point.
That goes back to my initial point. I rarely see people writing about having used Go for a traditional web app (I for one wouldn't as a first choice, it's way faster to build a web app using other platforms), which might be the reason why there are not enough eyes around session management and user authentication libraries. Just a hunch
Have you tried the golang dep tool for dependency management?
golang.github.io/dep/
Give it a try, it's a nice tool.
I have heard of it but I have not used it. Since go modules seems to be the official direction of the language I went with that for my project.
The dep tool is an official golang tool as well.
Yeah, go modules seems like the right direction, I think once it really catches on go will be pretty good in this category. It did seem a bit odd when I first started playing with go that you needed environment variables as a language requirement prior to go modules.
Go's true power comes when you need concurrency.
If you haven't tried it yet, try play around with Go channels. It greatly simplifies things.
I've done a bit and they are quite simple. As I'm sure I will use them more soon :)