Learning Functional Programming in Go
上QQ阅读APP看书,第一时间看更新

Iterating through a collection

In order to implement a collection, we must provide a way to access each element in the collection, which can be accomplished using the int index value shown in the following code. We will implement a first in, first out (FIFO) order queue. We will provide a way to store the elements using a slice data structure. Lastly, we will implement a Next() method to provide a way to traverse the elements in the collection.

In the following code, we define an interface for the Iterator object. It has one method, Next(), which will return the next element in the collection and a Boolean flag to indicate whether it's OK to continue iterating:

type CarIterator interface {
Next() (value string, ok bool)
}
const INVALID_INT_VAL = -1
const INVALID_STRING_VAL = ""

Next, we define a collection object that has two properties: an int index used to access the current element and a slice of strings, that is, the actual data in the collection:

type Collection struct {
index int
List []string
}

Now, we implement the collection's Next() method to meet the IntIterator interface's specification:

func (collection *Collection) Next() (value string, ok bool) {
collection.index++
if collection.index >= len(collection.List) {
return INVALID_STRING_VAL, false
}
return collection.List[collection.index], true
}

The newSlice function is the constructor for the iterable collection intCollection:

func newSlice(s []string) *Collection {
return &Collection{INVALID_INT_VAL, s}
}

Finally, we implement the main() function to test our Collection.

Let's open up a terminal window and use the .init toolset to run our simple Go application:

The . init ("Dot Init") toolset ensures that we have Go installed and that our GOPATH and GOBIN directories are properly configured. First, we source the init script by typing .init. Since we have no import statements, there is no need to run glide-update. To run our application, we type go-run. For more details about Dot Init, see the Appendix, Miscellaneous Information and How-Tos.

The problem with this implementation is that we are mixing what we want to do with how we do it. We implement an explicit for loop to perform the mechanics of the iteration. We define and mutate the value of the index value in order to traverse the elements. We can immediately see that this is an imperative implementation.

In functional programming, we declare what to, rather than imperatively implementing each detail of each operation. We also avoid the sequential nature of for loops, which are difficult to fit into a concurrent programming model.

Go is not a functional programming language, but it has a lot of functional features and we can leverage those features to write concise, expressive, and hopefully, bug-free code.

A pure functional language does not maintain a state. Function calls are often chained, where input is passed from function to function. Each function call transforms its input in some way. These functions do not need to be concerned about the external state and do not produce side effects. Each function call can be very efficient at what it does. This style of programming lends itself to efficient testing.

Next, we'll see how function chaining is a lot like piping output through Bash commands.