Member preview

Go Programming Language Analysis — I

Go Lang logo and Google (old) logo

A programming language is, in reality, a set of simple or complicated instructions in which we can instruct computers how to accomplish a task. Today we might be entertained by the fact that there are more than 1000 programming languages [1], and maybe, more are being created at the time of writing this paper. Why would anyone care about creating a new programming language, if we have plenty of them? There are two common reasons for that; learning purpose and/or to solve a problem. With the idea of solving a problem, that is where Go, a programming language developed by a group of engineers at Google, comes into action. They noticed that “no major systems languages has emerged in over a decade, but over that time the computing landscape has changed tremendously.” [2]. Go implements functionalities that are not well supported in other system programming languages. “The goals of the Go project were to eliminate the slowness and clumsiness of software Development at Google… The language was designed by and for people who write and read and debug and maintain large software systems.”

Go, unlike Lisp and other programming languages created mostly as research projects [4], was developed to create a better experience for engineers. Go might lack of some feature that modern programming languages have, but it tackles the problems that “make large-scale software development difficult.”. Some of these problems are: slow to compile, hard to read, cross-language builds, and more. Go was created for a very specific domain, but people have adopted it in multiple areas of software development because of its performance and it is very easy to read. Go was designed from scratch to be a language for system engineers at Google to use. [3] The support of concurrency and garbage collection is what makes Go big.

After spending a few hours learning the language, I noticed that the syntax that Go follow is very easy to read and understand. It is possible for a programmer to read Go code from left to right as plain English. An example would be the declaration of a function; it could be read as a function of name X that takes argument Y which is a type ‘T’, and it returns an integer, and then the body. This is the function signature, and in Go can be written as follows:

func nameX(Y int) int { // Body }

Unlike C, where the order follows type, name, arguments, and body, Go follows a pattern that is easy to pick. Although this is a more of a choice by the language designer, I believe this helps the new comers to pick up the language faster.

Let’s talk about objects in Go. Go is not fully object oriented, but this paradigm can be simulated in other ways with the tools that the programming language provides. In Go, there are no such things like classes or instances; those two concepts are referred as types. Go does not support inheritance which make programmers think differently when designing programs and implementing data structures. In Go, interfaces provide a different approach that the designers of the language “believe is easy to use and in some ways more general.”. Inheritance is implemented in a way that the programmer can “embed types in other types to provide something analogous but, no identical, to sub-classing” or inheritance.

In terms of designing and developing systems, this may sound a little trivial for programmers coming from languages like Java or C++. Thinking in a hierarchical way is not quite as easy in Go, although it can be simulated. The idea of having a base class, Person, and a sub-class of Person, Employee is not quite the same in Go. The designers of the language believe that fully object-oriented programming “involves too much discussion of the relationships between types, relationships that often could be derived automatically”. Huge systems have been built with these methodologies, but the bigger the system, the more complicated it is to debug and refactor due to, sometimes, very extended inheritance within objects. In reality, Go is able to simulate the behavior of an object-oriented programming language, but not the same way C++ or Java does it.

To solve this problem Go provides interfaces. Interfaces are a type that contains a set of method which are represented as signatures. This idea makes the programmer think about an object as a type. For instance, a type ‘T’ implements interface ‘I’. This can be translated as class ‘B’ is a subset of the class ‘A’. Now, let’s see what this means in Go. When a type implements an interface in Go, it is basically saying that the type implemented all the ‘required’ method signatures provided by the interface. This is, in other words, like signing a contract where we know for sure, that any of the methods in interface ‘I’ are implemented by type ‘T’. With that being said, we can now call any of those methods in interface ‘I’ through type T. In overall, the idea of sub-classing is technically illustrated via interfaces and the language allows you to use any type that implements ‘I’, where ‘I’ is expected. Go designers explained that this is more of a “structural typing that promotes separating of concerns and improves code re-use…”. [2]

Although structural typing is more flexible, it is a little hard to refactor/maintain [5]. Let say that you have an interface ‘I’, it contains a set of functions. For instance, let say that one of those functions is called ‘getFormattedID()’ and interface ‘I’ is implemented by hundreds or thousands of types. Suddenly you decided to change the name of the function to be ‘getID()’. This change will create errors in the client of type ‘T’, where those errors should have been detected beforehand in the implementation. This is an example of how structural typing can be, but Go’s compiler provides a great solution to this problem. If a type doesn’t implemented all the functions in an interface and it is used as it implemented it, the program will not compile. This will make sure that problem similar to this do not happen and make it ‘safe’ to produce changes in the code. This enforces programmers to make sure that they are, in fact, satisfying the interfaces that their types are supposed to implement and that a client is expecting later.

Go is a strong typing programming language. This means that once you declare a variable of type T, that variable cannot change its type in its life time. Although you do not have to include any type when declaring a variable using the short assignment operator ‘:=’, this variable will never be another type than type T. For example, if you do ‘foo := 7’ it is equivalent to ‘var foo int = 7’, but Go does the work for you. The reason they implemented the language that way, was because weak typing can bring a lot of errors in a large scale project. Also, Go does not support implicit numeric conversion. With that being said, it is not possible to implicitly convert a floating number to an integer. They believe that “the convenience of automatic conversion between numeric types in C is outweighed by the confusion it causes.”. I agree, this is like an open door for errors. Because of that, programmers will need to carefully convert numbers which bring more clearness to the code. Implicit numeric conversion “also complicates the compiler; ‘the usual arithmetic conversions’ are not easy to implement and inconsistent across architectures.”. [5] For portability reason they decide to avoid this complication and “at the cost of some explicit conversion in the code.” [5]. In my opinion, this could be a little bit annoying as a programmer, but in the long term, it is a very good rule to follow. This implies that any code written using explicit conversion can be easily followed.

Go’s package system is one of the main reasons that Go’s compiler is fast, relative to a C or C++ compiler. In Go packages’ name is the namespace for accessing the types or functions implemented in a specific package. [6] Go support encapsulation, in a very different way than C++ and Java. If a type or function in a package starts with an upper-case letter, that type of function is public Go’s package system forces programmer to only import packages that are really in use. If a package is imported but no used, then this will produce a compiler error. In my opinion, I think this is a good mechanism that importing unnecessary code. In programming languages like C and C++, preprocessors are in charge of importing code from other sources. The different between Go’s package system and C’s, is that the preprocessors copy, in other words merge, all the code together before sending it to the compiler. In Go this a lot different. Go creates objects of a package depending on the client’s necessities. For example, the math package contains a series of functions and packages. If I use the math.Sqrt function, and the Sqrt function have a dependency from other packages, i.e., math.Log, Go will implicitly create an object of math.Log and math.Sqrt. On the other hand, C preprocessors will copy all the code from the math library into the client, which will contain things that I do not even use in the lifetime of the program. In a small scale this is fine, but in large systems this is a big problem. This is why building medium and large scale programs takes very long to compile in languages like C and C++.

Another feature that distinguishes Go from system languages like C and C++, is its garbage collector. One of the biggest problems in a for programmers is memory management. The designers of Go made the decision to implement a garbage collector to help the programmer. The method used to implement Go garbage collector is detonated as mark-and-sweep collector. [13] This briefly means that the garbage collector is always marking objects that are being used, and sweeping unused ones. The idea is that the programmer should take advantage of this and focus on the development of the program itself than dealing with memory. Although this is a really good feature, it comes with a price. The designers of Go believe that this can be implemented without an outstanding cost. It is “critical to eliminate the programmer overhead, and advances in garbage collection technology…”. I agree, the implementation of programs can be simpler if the programmer does not have to manually deal with memory. Go’s designers believe that they “can implement it with low enough overhead and no significant latency.”. Go was designed to make concurrent applications easy to develop, and this garbage collector helps programmers a lot because it is very difficult to keep track of the memory resources while designing a concurrent application. By integrating a garbage collector to the language programmers can fully enjoy the advantage of Go concurrency built-in functionalities.

Go language design was very focused in concurrency. Concurrency can be interpreted as dealing with a lot of things at once, and this is what makes go a good choice if you are building applications that need to process different and/or unrelated things at once. A common mistake to of new Go programmer is that they think that currency is the same a parallelism or multi-threading, and it is not. Parallelism is more of a simultaneous execution of multiple things at once. [8] In Go there are two main built-in tools, goroutines and channels, that makes the language capable of doing this mechanism. A goroutine is a “lightweight thread of execution” [9] while channels are “pipes that connect” [10] goroutines. The designers of Go implemented this to enable what is called “concurrent decomposition” that can extremely increase the performance of any given task. This idea can be illustrated by taking a task and breaking it apart and for each piece of the task assign a worker, in the case of Go, a goroutine, to complete it. By doing so, the program can be better because the task was completed by multiple workers, all working in very specific tasks at once. The way that this workers communicate with each other is through channels. The idea of concurrent decomposition can be thought in multiple ways, and there is not really one solution in specific because it all depends on the problem that you are working on. Programmers can easily take advantages of this mechanism and write faster application by splitting task into parts.

In overall, Go is an incredible language that provides very useful functionalities for a system programming language. Like any other tool, Go is not perfect and has its opportunity cost, but I believe it is fun and worth learning. It is worth to mention that Go is a low-level programming language with high-level modern programming language features. This makes Go easy to use and it is notable that the interest of people all over the world is increasing. [11] Go is being used by different industries and it is a good language to develop large scale systems. [12] Although this might never happen, Go was originally created with the mission to be the substitution of C. If this is the case, I do not think this will happen anytime soon because of all the code that is written in C already. This is something that could happen and if you learn the language sooner than later, it will be an advantage for you as a developer.