Why I Like Go

I visited with PagerDuty yesterday for a little Friday beer and pizza. While there I got started talking about Go. I was asked by Alex, their CEO, why I liked it. Several other people have asked me the same question recently, so I figured it was worth posting.

Goroutines

The first 1/2 of Go's concurrency story. Lightweight, concurrent function execution. You can spawn tons of these if needed and the Go runtime multiplexes them onto the configured number of CPUs/Threads as needed. They start with a super small stack that can grow (and shrink) via dynamic allocation (and freeing). They are as simple as go f(x), where f() is a function.

Channels

The other 1/2 of Go's concurrency story. Channels are a typed conduit used to send and receive values. These are used to pass values to and from Goroutines for processing. They are, by default unbuffered, meaning synchronous and blocking. They can also be buffered, allowing values to queue up inside of them for processing. Multiple go routines can read/write to them at the same time w/o having to take locks. Go also has primitives for reading from multiple channels simultaneously via the select statement.

Compiled

Go compiles your program into a static binary. Yep, you read that correctly: a static binary. This makes deployment really simple, just copy over the binary. No bundler, no virtualenv, etc. All of that is handled at compile time. This simplifies deployment greatly.

Runtime

Go is a complied language, but still has a runtime. It handles all of the details of mallocing memory for you, allocating/deallocating space for variables, etc. Memory used by variables lasts as long as the variables are referenced, which is usually the scope of a function. Go has a garbage collector.

Pass By Value

Everything is passed by value, but there are pointers. It's just that the value of a pointer is a memory location, so it acts as pass by reference. This means that, by default, there is no shared state between functions. But, you can pass a pointer if you desire/need it for performance/memory utilization reasons. Go does the right thing by default, but doesn't shackle you. Oh, and there isn't any pointer math, so you won't screw yourself with it.

As pointed out on HN, you can do pointer math with the "unsafe" package and unsafe.Pointer.

Type System

Go has structs and interfaces. Go's structs can have methods associated with them. Structs can be anonymously included in other structs to make the inside struct's variables/methods available as part of the enclosting struct. Interfaces are sets of method signatures. Structs implement an interface by implementing the methods described by the interface definition. Functions can receive values by interface, like so. The compiler checks all of this at compile time.

Package System

Or lack there of. Since Go compiles everything statically, you don't have to worry about packages at runtime. But how do you include libraries into your code? Simply by importing them via url, like so: import "github.com/bmizerany/pq". Go's tooling knows how to look that up and clone the repo. Also works with Mercurial, Bazaar & Subversion.

Anonymous Functions & Closures

Go supports anonymous functions that can form closures. These functions can then be passed around and retain the environment under which they were created, like so. This can be super powerful when combined with channels and go rountines.

Built in profiling

It supports pprof as part of the standard library. And with a very small bit of work, you can access profiling info via a http interface.

Defer

Ever forget to close a file descriptor or free some memory? So have the designers of Go. The reason for this is that you usually have to perform those actions far away from where the resources were opened/allocated. Go solves this problem with the defer statement. Each defer statement pushes a function/expression onto a stack that get's executed in LIFO order after the enclosing function returns. This makes cleanup really easy, after opened a file, just defer file.Close().

This comment has been minimized.

"Pass By Value"
Most modern programming languages are 'Pass By Value'. It's not really a feature of Go.
The interesting part about Go is that it has user definable value types, which is becoming less common in programming languages.

"Pass By Value"
Most modern programming languages are 'Pass By Value'. It's not really a feature of Go.
The interesting part about Go is that it has user definable value types, which is becoming less common in programming languages.

This comment has been minimized.

(C++ programmer since <2004 here) @DAddYE: I thought so too about the lack of meta-programming. Then I realised the genius of the go language authors. I haven't seen them say as much explicitly, but I think it's intentionally missing. Since you don't really need it for most problems you thought you did. The resulting code is much easier to cleaner, which is really a win when you have many people using and working on the same codebase. It also makes the tooling simpler and blazingly fast. The whole go compiler suite compiles in 8 seconds on my machine. Can you say the same about your favourite C++ compiler?

The resulting code might be a little larger and may even suffer from one or two copy-paste bugs if you really need to use multiple types. But it is much simpler (less indirection, more straightforward). Taken globally across all go code I believe this is a big win.

(C++ programmer since <2004 here) @DAddYE: I thought so too about the lack of meta-programming. Then I realised the genius of the go language authors. I haven't seen them say as much explicitly, but I think it's intentionally missing. Since you don't really need it for most problems you thought you did. The resulting code is much easier to cleaner, which is really a win when you have many people using and working on the same codebase. It also makes the tooling simpler and blazingly fast. The whole go compiler suite compiles in 8 seconds on my machine. Can you say the same about your favourite C++ compiler?

The resulting code might be a little larger and may even suffer from one or two copy-paste bugs if you really need to use multiple types. But it is much simpler (less indirection, more straightforward). Taken globally across all go code I believe this is a big win.

This comment has been minimized.

I think your con of only 1 CPU by default is a good thing. You have to opt-in for concurrency with goroutines and now also opt in for parallelism but both of them are pretty much hands free opt-ins with no strain on the developer.

I think your con of only 1 CPU by default is a good thing. You have to opt-in for concurrency with goroutines and now also opt in for parallelism but both of them are pretty much hands free opt-ins with no strain on the developer.

This comment has been minimized.

@codeinchaos: I've found very little need to invent custom collections in most of the problems I've needed to solve. I wouldn't say go is a silver bullet, but there are some areas where it really excels. I find that in C++ you can end up in a complicated mess of templated types. In contrast, most go code is very everything is flat, you can reason about a chunk of code just by looking at it, interfaces just work (it's essentially statically valid duck typing, which is awesome), and I don't miss spending time trying to invent, decipher or debug crazy template types.

@codeinchaos: I've found very little need to invent custom collections in most of the problems I've needed to solve. I wouldn't say go is a silver bullet, but there are some areas where it really excels. I find that in C++ you can end up in a complicated mess of templated types. In contrast, most go code is very everything is flat, you can reason about a chunk of code just by looking at it, interfaces just work (it's essentially statically valid duck typing, which is awesome), and I don't miss spending time trying to invent, decipher or debug crazy template types.

This comment has been minimized.

Go compiled with the golang compiler does indeed compile to static executables, but these don't need a libc - they are static, the runtime is a "custom" runtime in the static executable which is not the libc.

Go compiled with the golang compiler does indeed compile to static executables, but these don't need a libc - they are static, the runtime is a "custom" runtime in the static executable which is not the libc.

This comment has been minimized.

Im going to address each one of the reserves given in the talk and comments.

Garbage Collection- While the garbage collection in Go is currently being rewritten, it should be kept in mind that the user can definitely circumvent how much the current garbage collector is run. The garbage collector is only run under certain conditions.

A. The garbage collector hasnt run in 2 minutes.
B. The runtime allocates 2x's the amount of memory allocated present during the last garbage collection.
C. The finalizer is activated.
D. The user explicitly calls runtime.GC()
E. The program exits.

The garbage collector constantly evolves over time and as programs scale in C++, developers typically build their own memory management systems. The difference is that in Go, between compiles using new versions of Go the improvements are there. The C++ management systems require maintenance of the project's authors. Which means either they get better, or they dont.

Generics- The one argument that I hear most about go's failing pertains to Go's lack of generics. I assume that this typically means a lack of templates. The problem with templates in C++ is that it greatly complicates the compilation of programs. Essentially adding C++ like templates in Go would mean that Go would have to compromise the main reason to create Go in the first place. Speedy compilations. Its the argument of programmer convenience vs programmer productivity when programs reach millions of lines of code. The reality is that when programs scale, often we developers are having to come up with hacky ways to improve the "system" of development. Take into consideration facebook's HipHop php compiler, or consider google's parallel build systems. Each "hacky" solution adds complexity and potential bugs that complicate all of our lives.

The more complicated development is for whatever convenience it adds in the short term is not worth valuing in my opinion. Generics for Go doesnt make sense because it conflicts with KISS philosophy. For any convenience you lose in having one less paradigm, you gain back much more convenience when your software scales. Don't like it? Then use C++ and suffer.

Meta Programming- Alot of this argument falls back into the generics section. Though nothing stops users of Go, from using projects like https://github.com/droundy/gotgo or even using the c processor by hand. I personally never needed to use templates or CPP. It only complicates the build system. Its not worth compromising the values of Go.

In my honest opinion, the only argument to not use Go is that it is TOO convenient. It makes working on other non Go projects miserable. Trust me, try Go for 6 months and then try going back to C#, C++, C, Python.

Im going to address each one of the reserves given in the talk and comments.

Garbage Collection- While the garbage collection in Go is currently being rewritten, it should be kept in mind that the user can definitely circumvent how much the current garbage collector is run. The garbage collector is only run under certain conditions.

A. The garbage collector hasnt run in 2 minutes.
B. The runtime allocates 2x's the amount of memory allocated present during the last garbage collection.
C. The finalizer is activated.
D. The user explicitly calls runtime.GC()
E. The program exits.

The garbage collector constantly evolves over time and as programs scale in C++, developers typically build their own memory management systems. The difference is that in Go, between compiles using new versions of Go the improvements are there. The C++ management systems require maintenance of the project's authors. Which means either they get better, or they dont.

Generics- The one argument that I hear most about go's failing pertains to Go's lack of generics. I assume that this typically means a lack of templates. The problem with templates in C++ is that it greatly complicates the compilation of programs. Essentially adding C++ like templates in Go would mean that Go would have to compromise the main reason to create Go in the first place. Speedy compilations. Its the argument of programmer convenience vs programmer productivity when programs reach millions of lines of code. The reality is that when programs scale, often we developers are having to come up with hacky ways to improve the "system" of development. Take into consideration facebook's HipHop php compiler, or consider google's parallel build systems. Each "hacky" solution adds complexity and potential bugs that complicate all of our lives.

The more complicated development is for whatever convenience it adds in the short term is not worth valuing in my opinion. Generics for Go doesnt make sense because it conflicts with KISS philosophy. For any convenience you lose in having one less paradigm, you gain back much more convenience when your software scales. Don't like it? Then use C++ and suffer.

Meta Programming- Alot of this argument falls back into the generics section. Though nothing stops users of Go, from using projects like https://github.com/droundy/gotgo or even using the c processor by hand. I personally never needed to use templates or CPP. It only complicates the build system. Its not worth compromising the values of Go.

In my honest opinion, the only argument to not use Go is that it is TOO convenient. It makes working on other non Go projects miserable. Trust me, try Go for 6 months and then try going back to C#, C++, C, Python.

This comment has been minimized.

This comment has been minimized.

In my honest opinion, the only argument to not use Go is that it is TOO convenient. It makes working on other non
Go projects miserable. Trust me, try Go for 6 months and then try going back to C#, C++, C, Python.

So true! I have to agree with @mortdeus, I have been on the scene since the days of C and Turbo Pascal and Go is a significant breeze of fresh air after the (current) Java/C# super bubble

In my honest opinion, the only argument to not use Go is that it is TOO convenient. It makes working on other non
Go projects miserable. Trust me, try Go for 6 months and then try going back to C#, C++, C, Python.

So true! I have to agree with @mortdeus, I have been on the scene since the days of C and Turbo Pascal and Go is a significant breeze of fresh air after the (current) Java/C# super bubble

This comment has been minimized.

I'm a self taught web developer which means that I have a few gaps in my knowledge base. I'm coming from 5+ years of C/Python/Javascript and have decided to pick Go as my next language just to keep up on things.

So far it's awesome. I like the fact that everything I want a general purpose language to do Go does. I can easily see my 'everything-stack' being reduced from Python/Redis/Celery to just Go, which is awesome. Don't get me wrong though, Python/Redis/Celery is an awesome stack that I've had in production for years now, but the thought of getting all that functionality with just Go is very very enticing.

Not to mention something about writing Go just feels good. It's a very aesthetic language in my opinion.

I'm a self taught web developer which means that I have a few gaps in my knowledge base. I'm coming from 5+ years of C/Python/Javascript and have decided to pick Go as my next language just to keep up on things.

So far it's awesome. I like the fact that everything I want a general purpose language to do Go does. I can easily see my 'everything-stack' being reduced from Python/Redis/Celery to just Go, which is awesome. Don't get me wrong though, Python/Redis/Celery is an awesome stack that I've had in production for years now, but the thought of getting all that functionality with just Go is very very enticing.

Not to mention something about writing Go just feels good. It's a very aesthetic language in my opinion.

This comment has been minimized.

In earlier releases of Go, the default value was 1, but as of Go 1.5 the default value is the number of cores available. Therefore programs compiled after 1.5 should demonstrate parallel execution of multiple goroutines.

In earlier releases of Go, the default value was 1, but as of Go 1.5 the default value is the number of cores available. Therefore programs compiled after 1.5 should demonstrate parallel execution of multiple goroutines.