http://www.developer.com/

Back to article

Anatomy of a Google Go Program


February 3, 2010

"Go" is a systems programming language developed by Google software engineers, who set out to create a simple, fast alternative for building systems software and software components that could run natively on various hardware platforms. As Go is a compiled language, its programs are linked, compiled, and executed similarly to C programs.

Two Go compilers currently are available, one for the x86 platform and another for AMD. This approach allows you to compile source programs using the command 8g (8 for x86) or 6g (6 for AMD). However, to enable programs to be compiled by Go, you must link them to an out executable with the command 8l or 6l.

In this article, I will demonstrate Go's features and syntax through a very simple, but very common, scenario: reading in lines of multiple, space-delimited records from a text file and processing each line concurrently. I will use the Go compiler and linker for the x86 platform.

Organization of Programs

Go programs are organized in packages. In the demo program, I declare two packages: main and acct. By Go conventions, main is a package that contains an executable main function and acct contains accounting routines referenced by main. To refer to a function from a package, you simply prefix the name of the function with a package name. Packages in Go serve as the classes for routines. To avoid naming collisions, you can custom name packages after importing them.

import format "fmt";


Despite being a small language, Go incorporates a significant number of very useful packages, including XML, JSON, HTTP and web services processing.

To import a custom package, in this case acct, specify:

import "./acct"


No Parenthesis and Semicolons Sometimes

Control statements in Go do not accept parenthesis. While the most common control statement, if, would take this form in C, C++, C# or Java:

if ( count > MAX_ITEMS ) {


In Go, it would have the following form:

if count > MAX_ITEMS {


Semicolons in Go are optional. A single statement does not require a semicolon at all. Semicolons are required only in control blocks with multiple statements, where they delineate every statement from the previous one. The last statement in the list does not require a semicolon.

for j := 0; j<3; j++  {
           localSum := <-sum;            
           fmt.Printf( "sum is %f \n", localSum )
}


However, you can terminate any Go statement with a semicolon.

Types, Objects and Memory Management

Go lacks strength as a typed language. You do not need to explicitly declare types of variables. Instead, Go implicitly assigns the type to the untyped variable when the value is first assigned to the variable.

You can declare variables explicitly:

var newline string;


Or declare and initialize them implicitly with a := assignment.

newLine := string(contents);


In this case, the newLine variable is declared as a string and assigned a string value of the contents byte array.

When you have declared the type of the variable, you cannot re-declare it. So, the following would result in an error:

sum = total + 1;
newLine = sum;


You declare and allocate structs and other non-primitive objects with the familiar keyword new. Go supports referencing types by value or by reference using the & prefix, which will be familiar to anyone who has programmed in C or C++.

You declare and allocate maps, arrays, and channels with the keyword make, which greatly resembles the syntax of malloc; it accepts type and optionally the size of the allocated object.

In this example, I allocate a channel variable called sum, which I will use to pass data between the worker threads (goroutines) and the server:

sum = make (channel float, 50 )


I will address channels and concurrency shortly.

Because Go is a garbage-collected language, you do not need to manually de-allocate dynamically allocated variables. Go's garbage collector de-allocates the memory of unreferenced variables.

Functions

Go has a peculiar function syntax and implementation. Its functions are declared with the keyword func followed by parameters and return types. So, you would declare a function that takes a channel of float types and returns a float like this:

func CalculateTotal(channel float) float{ ...


More interestingly, Go functions can return more than one type. So, Go native functions commonly return either error types or expected, normally produced types in the same invocation. Think of errors in this case as exceptions in Java or C#.

This is how I invoke the file-reading routine, which can produce a reading error:

contents, err := ioutil.ReadFile( "test.txt" );
 
if err != nil{
    fmt.Sprintf( "Error parsing file e %q", err );
    return;
}


Notice that in addition to the expected result from a ReadFile (byte array), I also accept the err type, which is returned when the ReadFile fails.

Array Slices and Automatic For Loops

In the demo program, I process each line of the file as a space-separated record of fields. I first split the contents of the file into lines using the split function available in the strings package:

lines := strings.Split( contents, "\n", 0 );


Next, I split each line of the file into constituting fields:

fields := strings.Split( line, " ", 0 );


The 0 in the invocation of the Split function indicates how many elements should be returned from that function. The 0 indicates that all substrings should be returned.

Slices

I wrote the demo program so that invocation of the Split function returns an array of all seven elements. I did this for demonstrational purposes, so I can showcase one of the more interesting features of Go's array processing: slices.

Slices allow for arbitrary slicing of arrays. In this case, to access only the first five of the loadedField array, I cut out the desired parts of the array using the following slice syntax:

fields = loadedFields[0:4];


Note that the remaining elements outside of the slice still remain allocated until the entire array is not referenced and available for garbage collection.

Concurrency, Channels and Goroutines

Concurrent programming in Go is very straightforward. The simplest form (and the one demonstrated here) is to execute a regular function as a concurrent routine using the keyword go. In Go, such routines are called "goroutines."

In the demo program, I process each file line concurrently. So, to "fork" off a new goroutine, I call the function acct.CalculateBonus prefixed with the keyword go:

go acct.CalculateBonus( line, sum );


Note the second parameter to the function, sum. This is a channel variable of type float. Channels are pipe-like constructs used to pass values in a thread-safe fashion between two or more concurrent routines.

Recall this code I mentioned previously:

sum = make( channel float, 50 )


Here, sum is declared as a channel of float types that can hold 50 of the variables at the same time in the channel. In other terms, sum is a synchronized buffer of capacity 50.

The invoked acct.CalculateBonus routine places the results of the calculation into a sum channel, like this:

Acct.CalculateBonuses( line, sum ){
 
bonus = line[ 4 ] * 0.25 * 4000;
sum <- bonus;


To retrieve the value from the invoking routine, I pull the value from the sum channel with this command:

totalPaid += <-sum;


Where Will "Go" Go from Here?

I have demonstrated here a very brief survey of some common but very useful features of the Go programming language. These few demonstrated features are by no means all that Go has to offer, but I hope they effectively illustrate the nature of the language as well as its philosophy of implementing features from both well established as well as the most recent existing languages.

Go is a modern, fast and concise language that will appeal to most developers. The question with Go is not whether its syntax is elegant or its features rich but rather whether it has a future. No one can tell yet if Go will enjoy broad enough adoption to become a mainstream language. Given that it is very nicely designed and sponsored by Google, it certainly will enjoy adoption at least at Google. However, a fledgling programming language truly comes into its own when the broader programming community embraces it. Therefore, it is up to us developers to explore this language and its features and adopt it if it so fits.

Code Download

  • GoProgram_src.zip

    For Further Reading

  • The Go Programming Language homepage

    About the Author

    Edmon Begoli is a software architect with 14 years of professional experience on large commercial and public software projects. He is a member of the R&D staff at Oak Ridge National Laboratory.
  • Sitemap | Contact Us

    Thanks for your registration, follow us on our social networks to keep up-to-date