© Dimitar Dimitrov, 123RF.com
Programming with Go
Modern Classic
Go makes no false promises. Perhaps that is exactly what makes the language so interesting in the long term. In 2007, Robert Griesemer, Ken Thompson, and Rob Pike invented Go because they were unhappy with the existing languages for system programming. They do not pander to any of the current trends like asynchronous web programming or cloud computing; instead, they have learned from 30 years of experience with C and created a programming language capable of becoming its successor.
Like C, Go [1] shows its strengths in system programming, although the language can be deployed for almost any purpose. The language inventors are employed by Google, and Go has made its way onto the Google App Engine just like Java and Python; however, support for Go is currently in the experimental stage [2].
Future Promise
Early in 2008, Thompson completed his first experimental compiler, which generates C code. Ian Taylor started a little later with work on a Go front end for the GCC compiler. Toward the end of the year, Russ Cox joined the Go project, and the work went forward a little faster. In November 2009, the team finally presented the first public release of the Go compiler. In March 2012, version 1.0 of the compiler and the specification were released, promising compatibility with future Go releases [3]. Go is thus suitable right now for genuine software projects, not just experiments.
The declared goals of the Go project are efficient compilation, fast execution, and simple programming. Existing languages fail to combine all three, say the Go inventors. Go sets out to combine the simple programming offered by increasingly popular languages such as Python and Ruby with the efficiency and reliability of languages such as C, C++, and Java. In so doing, compilation will not take as long as Java projects, for example. Additionally, Go aims to handle dependencies between external libraries in a superior way.
Simplicity is one of the outstanding features of Go, so the language's inventors have done without many constructs. First and foremost, Go aims to have a consistent and unambiguous syntax. This cannot be said of languages such as Perl, Ruby, or Scala, which use a variety of syntactic constructs or methods for one and the same purpose.
Go is oriented on C but omits many elements that redundantly fulfill the same purpose. For example, Go uses only the Postfix variant of the increment operator ++, which sits behind the variable. At the same time, this is only a statement, not an expression that can be used downstream. Although this leads to a little more typing, it does ensure clear semantics and less confusion.
Go programs are made even more clear by borrowing from structured languages such as Pascal, Modula, and Oberon. In part, the Go syntax was realized in Newsqueak and limbo. The latter is the programming language of the Inferno operating system, which in turn is an offshoot of the Plan9 system, on which Thompson and Pike worked previously.
Clearly Formatted
For example, Go does without semicolons to complete individual statements (Figure 1). Although semicolons are part of the language specification, the parser adds them independently, the way the JavaScript specification does. For this to work, programmers need to keep to the specified style of bracketing, which states that opening brackets in a block always appear at the end of a line, not on their own at the beginning of the following line. The gofmt tool provide with the package checks for this formatting, removing the need for the programmer to do so and ensuring that Go code looks the same in all projects.
Variables are declared with the keyword var, followed by the variable name and the type, that is, opposite the order in C, C++, or Java:
var x float64
Variable names can start with any character regarded as a letter in the Unicode standard. The := operator lets you define and initialize variables in one fell swoop. The var keyword can be omitted here, as in most type specifications:
i := 1 pi := 3.142
The Go developers also had a radical clean-out when it came to loops – if you take a closer look, typically all the whiles, do untils, foreachs, and so on can be reworded. Go has only one loop: the good old for loop.
Types
From the increasing popularity of languages like Python and JavaScript, the developers deduced that a simple type system will tend to promote the popularity of a programming language. As Robert Griesemer puts it: "Clumsy type systems drive people to dynamically typed languages" [4]. However, they were not prepared to sacrifice the reliability that strongly typed languages offer. The classical compromise in Go is that strong typing is implemented with type inference – that is to say, any variable has a fixed type, but the programmer does not need to specify it if the compiler naturally understands the type.
At the same time, Go does not have a strict type hierarchy. In the view of the developers, this makes the implementation of compilers and tools more complicated and leads to endless discussions on the concrete shaping of the hierarchy. Instead, Go offers important types, such as arrays, and maps (hashes) in the language core. Pointers also exist, but more like those in Pascal (i.e., without the typical C pointer arithmetic).
Generic types made popular by C++, Java, and Python are not supported in Go, even if the developers admit they can be useful, and their implementation is not unequivocally ruled out some time in the distant future.
Nevertheless, Go does support object-oriented programming; the abstraction for this is interfaces, but the interface in Go differs from its counterparts in Java or Objective C. An interface specifies a collection of methods that implement "objects" to fulfill a certain function, without belonging to a common class.
Also, Go has no exceptions because they cause uncontrollable changes in the program flow. As an alternative, Go offers multiple return values for functions, which can use a wildcard mechanism to capture error values. Line 2 of Listing 1 shows a call to os.Create(), which at the same time returns the file handle and an error code.
Listing 1
Error Handling
01 for try := 0; try < 2; try++ {
02 file, err = os.Create(filename)
03 if err == nil {
04 return
05 }
06 if e, ok := err.(*os.PathError); ok && e.Err == syscall.ENOSPC {
07 deleteTempFiles() // Recover some space.
08 continue
09 }
10 return
11 }
Buy this article as PDF
(incl. VAT)