Libraries
Until now, most of our examples were applications. An application is defined by its main
function and package. But with Go, you can also create pure libraries. In libraries, the package need not be called main nor do you need the main
function.
As libraries aren't applications, you cannot build a binary file with them and you need the main
package that is going to use them.
For example, let's create an arithmetic library to perform common operations on integers: sums, subtractions, multiplications, and divisions. We'll not get into many details about the implementation to focus on the particularities of Go's libraries:
package arithmetic func Sum(args ...int) (res int) { for _, v := range args { res += v } return }
First, we need a name for our library; we set this name by giving a name to the entire package. This means that every file in this folder must have this package name too and the entire group of files composes the library called arithmetic too in this case (because it only contains one package). This way, we won't need to refer to the filenames for this library and to provide the library name and path will be enough to import and use it. We have defined a Sum
function that takes as many arguments as you need and that will return an integer that, during the scope of the function, is going to be called res
. This allows us to initialize to 0
the value we're returning. We defined a package (not the main
package but a library one) and called it arithmetic
. As this is a library package, we can't run it from the command line directly so we'll have to create the main
function for it or a unit test file. For simplicity , we'll create a main
function that runs some of the operations now but let's finish the library first:
func Subtract(args ...int) int { if len(args) < 2 { return 0 } res := args[0] for i := 1; i < len(args); i++ { res -= args[i] } return res }
The Subtraction
code will return 0
if the number of arguments is less than zero and the subtraction of all its arguments if it has two arguments or more:
func Multiply(args ...int) int { if len(args) < 2 { return 0 } res := 1 for i := 0; i < len(args); i++ { res *= args[i] } return res }
The Multiply
function works in a similar fashion. It returns 0
when arguments are less than two and the multiplication of all its arguments when it has two or more. Finally, the Division
code changes a bit because it will return an error if you ask it to divided by zero:
func Divide(a, b int) (float64, error) { if b == 0 { return 0, errors.New("You cannot divide by zero") } return float64(a) / float64(b), nil }
So now we have our library finished, but we need a main
function to use it as libraries cannot be converted to executable files directly. Our main function looks like the following:
package main import ( "fmt" "bitbucket.org/mariocastro/go-design-patterns/introduction/libraries/arithmetic" ) func main() { sumRes := arithmetic.Sum(5, 6) subRes := arithmetic.Subtract(10, 5) multiplyRes := arithmetic.Multiply(8, 7) divideRes, _ := arithmetic.Divide(10, 2) fmt.Printf("5+6 is %d. 10-5 is %d, 8*7 is %d and 10/2 is %f\n", sumRes, subRes, multiplyRes, divideRes) }
We are performing an operation over every function that we have defined. Take a closer look at the import
clause. It is taking the library we have written from its folder within $GOPATH
that matches its URL in
https://bitbucket.org/
. Then, to use every one of the functions that are defined within a library, you have to name the package name that the library has before each method.
Note
Have you realized that we called our functions with uppercase names? Because of the visibility rules we have seen before, exported functions in a package must have uppercase names or they won't be visible outside of the scope of the package. So, with this rule in mind, you cannot call a lowercase function or variable within a package and package calls will always be followed by uppercase names.
Let's recall, some naming conventions about libraries:
- Each file in the same folder must contain the same package name. Files don't need to be named in any special way.
- A folder represents a package name within a library. The folder name will be used on import paths and it doesn't need to reflect the package name (although it's recommended for the parent package).
- A library is one or many packages representing a tree that you import by the parent of all packages folder.
- You call things within a library by their package name.