Chapter 4. bundles of code: Packages

image

It’s time to get organized. So far, we’ve been throwing all our code together in a single file. As our programs grow bigger and more complex, that’s going to quickly become a mess.

In this chapter, we’ll show you how to create your own packages to help keep related code together in one place. But packages are good for more than just organization. Packages are an easy way to share code between your programs. And they’re an easy way to share code with other developers.

Different programs, same function

We’ve written two programs, each with an identical copy of a function, and it’s becoming a maintenance headache...

On this page, we’ve got a new version of our pass_fail.go program from Chapter 2. The code that reads a grade from the keyboard has been moved to a new getFloat function. getFloat returns the floating-point number the user typed, unless there’s an error, in which case it returns 0 and an error value. If an error is returned, the program reports it and exits; otherwise, it reports whether the grade is passing or failing, as before.

image

On this page, we’ve got a new tocelsius.go program that lets the user type a temperature in the Fahrenheit measurement system and converts it to the Celsius system.

Notice that the getFloat function in tocelsius.go is identical to the getFloat function in pass_fail.go.

image

Sharing code between programs using packages

image
func getFloat() (float64, error) {
       reader := bufio.NewReader(os.Stdin)
       input, err := reader.ReadString('\n')
       if err != nil {
              return 0, err
       }
       input = strings.TrimSpace(input)
       number, err := strconv.ParseFloat(input, 64)
       if err != nil {
             return 0, err
       }
       return number, nil
}

Actually, there is something we can do—we can move the shared function to a new package!

Go allows us to define our own packages. As we discussed back in Chapter 1, a package is a group of code that all does similar things. The fmt package formats output, the math package works with numbers, the strings package works with strings, and so on. We’ve used the functions from each of these packages in multiple programs already.

Being able to use the same code between programs is one of the major reasons packages exist. If parts of your code are shared between multiple programs, you should consider moving them into packages.

If parts of your code are shared between multiple programs, you should consider moving them into packages.

The Go workspace directory holds package code

Go tools look for package code in a special directory (folder) on your computer called the workspace. By default, the workspace is a directory named go in the current user’s home directory.

The workspace directory contains three subdirectories:

  • bin, which holds compiled binary executable programs. (We’ll talk more about bin later in the chapter.)

  • pkg, which holds compiled binary package files. (We’ll also talk more about pkg later in the chapter.)

  • src, which holds Go source code.

Within src, code for each package lives in its own separate subdirectory. By convention, the subdirectory name should be the same as the package name (so code for a gizmo package would go in a gizmo subdirectory).

Each package directory should contain one or more source code files. The filenames don’t matter, but they should end in a .go extension.

image

there are no Dumb Questions

Q: You said a package folder can contain multiple files. What should go in each file?

A: Whatever you want! You can keep all of a package’s code in one file, or split it between multiple files. Either way, it will all become part of the same package.

Creating a new package

Let’s try setting up a package of our own in the workspace. We’ll make a simple package named greeting that prints greetings in various languages.

The workspace directory isn’t created by default when Go is installed, so you’ll need to create it yourself. Start by going to your home directory. (The path is C:\Users\<yourname> on most Windows systems, /Users/<yourname> on Macs, and /home/<yourname> on most Linux systems.) Within the home directory, create a directory named go—this will be our new workspace directory. Within the go directory, create a directory named src.

Finally, we need a directory to hold our package code. By convention, a package’s directory should have the same name as a package. Since our package will be named greeting, that’s the name you should use for the directory.

We know, that seems like a lot of nested directories (and actually, we’ll be nesting them even deeper shortly). But trust us, once you’ve built up a collection of packages of your own as well as packages from others, this structure will help you keep your code organized.

image

And more importantly, this structure helps Go tools find the code. Because it’s always in the src directory, Go tools know exactly where to look to find code for the packages you’re importing.

Your next step is to create a file within the greeting directory, and name it greeting.go. The file should include the code below. We’ll talk about it more shortly, but for now there’s just a couple things we want you to notice...

Like all of our Go source code files thus far, this file starts with a package line. But unlike the others, this code isn’t part of the main package; it’s part of a package named greeting.

image

Also notice the two function definitions. They aren’t much different from other functions we’ve seen so far. But because we want these functions to be accessible outside the greeting package, notice that we capitalize the first letter of their names so the functions are exported.

Importing our package into a program

Now let’s try using our new package within a program.

image

In your workspace directory, within the src subdirectory, create another subdirectory named hi. (We don’t have to store code for executable programs within the workspace, but it’s a good idea.)

Then, within your new hi directory, we need to create another source file. We can name the file anything we want, as long as it ends with a .go extension, but since this is going to be an executable command, we’ll name it main.go. Save the code below within the file.

Like in every Go source code file, this code starts with a package line. But because we intend this to be an executable command, we need to use a package name of main. Generally, the package name should match the name of the directory it’s kept in, but the main package is an exception to that rule.

image

Next we need to import the greeting package so we can use its functions. Go tools look for package code in a folder within the workspace’s src directory whose name matches the name in the import statement. To tell Go to look for code in the src/greeting directory within the workspace, we use import "greeting".

Finally, because this is code for an executable, we need a main function that will be called when the program runs. In main we call both functions that are defined in the greeting package. Both calls are preceded by the package name and a dot, so that Go knows which package the functions are a part of.

image

We’re all set; let’s try running the program. In your terminal or command prompt window, use the cd command to change to the src/hi directory within your workspace directory. (The path will vary based on the location of your home directory.) Then, use go run main.go to run the program.

When it sees the import "greeting" line, Go will look in the greeting directory in your workspace’s src directory for the package source code. That code gets compiled and imported, and we’re able to call the greeting package’s functions!

Packages use the same file layout

Remember back in Chapter 1, we talked about the three sections almost every Go source code file has?

image

That rule holds true for the main package in our main.go file, of course. In our code, you can see a package clause, followed by an imports section, followed by the actual code for our package.

image

Packages other than main follow the same format. You can see that our greeting.go file also has a package clause, imports section, and the actual package code at the end.

image

Breaking Stuff is Educational!

image

Take our code for the greeting package, as well as the code for the program that imports it. Try making one of the changes below and run it. Then undo your change and try the next one. See what happens!

image
image

Pool Puzzle

Your job is to take code snippets from the pool and place them into the blank lines. Don’t use the same snippet more than once, and you won’t need to use all the snippets. Your goal is to set up a calc package within a Go workspace so calc’s functions can be used within main.go.

image
image

Note: each snippet from the pool can only be used once!

image Answers in “Pool Puzzle Solution”.

Package naming conventions

Developers using a package are going to need to type its name each and every time they call a function from that package. (Think of fmt.Printf, fmt.Println, fmt.Print, etc.) To make that as painless as possible, there are a few rules package names should follow:

  • A package name should be all lowercase.

  • The name should be abbreviated if the meaning is fairly obvious (such as fmt).

  • It should be one word, if possible. If two words are needed, they should not be separated by underscores, and the second word should not be capitalized. (The strconv package is one example.)

  • Imported package names can conflict with local variable names, so don’t use a name that package users are likely to want to use as well. (For example, if the fmt package were named format, anyone who imported that package would risk conflicts if they named a local variable format).

Package qualifiers

When accessing a function, variable, or the like that’s exported from a different package, you need to qualify the name of the function or variable by typing the package name before it. When you access a function or variable that’s defined in the current package, however, you should not qualify the package name.

In our main.go file, since our code is in the main package, we need to specify that the Hello and Hi functions are from the greeting package, by typing greeting.Hello and greeting.Hi.

image

Suppose that we called the Hello and Hi functions from another function in the greeting package, though. There, we would just type Hello and Hi (without the package name qualifier) because we’d be calling the functions from the same package where they’re defined.

Moving our shared code to a package

Now that we understand how to add packages to the Go workspace, we’re finally ready to move our getFloat function to a package that our
pass_fail.go and tocelsius.go programs can both use.

image

Let’s name our package keyboard, since it reads user input from the keyboard. We’ll start by creating a new directory named keyboard inside our workspace’s src directory.

Next, we’ll create a source code file within the keyboard directory. We can name it anything we want, but we’ll just name it after the package: keyboard.go.

At the top of the file, we’ll need a package clause with the package name: keyboard.

Then, because this is a separate file, we’ll need an import statement with all the packages used in our code: bufio, os, strconv, and strings. (We need to leave out the fmt and log packages, as those are only used in the pass_fail.go and tocelsius.go files.)

image

Finally, we can copy the code from the old getFloat function as is. But we need to be sure to rename the function to GetFloat, because it won’t be exported unless the first letter of its name is capitalized.

Now the pass_fail.go program can be updated to use our new keyboard package.

image

Because we’re removing the old getFloat function, we need to remove the unused bufio, os, strconv, and strings imports. In their place, we’ll import the new keyboard package.

In our main function, in place of the old call to getFloat, we’ll call the new keyboard.GetFloat function. The rest of the code is unchanged.

If we run the updated program, we’ll see the same output as before.

image

We can make the same updates to the tocelsius.go program.

We update the imports, remove the old getFloat, and call keyboard.GetFloat instead.

And again, if we run the updated program, we’ll get the same output as before. But this time, instead of relying on redundant function code, we’re using the shared function in our new package!

Constants

Many packages export constants: named values that never change.

A constant declaration looks a lot like a variable declaration, with a name, optional type, and value for the constant. But the rules are slightly different:

  • Instead of the var keyword, you use the const keyword.

  • You must assign a value at the time the constant is declared; you can’t assign a value later as with variables.

  • Variables have the := short variable declaration syntax available, but there is no equivalent for constants.

image

As with variable declarations, you can omit the type, and it will be inferred from the value being assigned:

image

The value of a variable can vary, but the value of a constant must remain constant. Attempting to assign a new value to a constant will result in a compile error. This is a safety feature: constants should be used for values that shouldn’t ever change.

image

If your program includes “hardcoded” literal values, especially if those values are used in multiple places, you should consider replacing them with constants (even if the program isn’t broken up into multiple packages). Here’s a package with two functions, both featuring the integer literal 7 representing the number of days in a week:

image

By replacing the literal values with a constant, DaysInWeek, we can document what they mean. (Other developers will see the name DaysInWeek, and immediately know we didn’t randomly choose the number 7 to use in our functions.) Also, if we add more functions later, we can avoid inconsistencies by having them refer to DaysInWeek as well.

Notice that we declare the constant outside of any function, at the package level. Although it’s possible to declare a constant inside a function, that would limit its scope to the block for that function. It’s much more typical to declare constants at the package level, so they can be accessed by all functions in that package.

image

As with variables and functions, a constant whose name begins with a capital letter is exported, and we can access it from other packages by qualifying its name. Here, a program makes use of the DaysInWeek constant from the main package by importing the dates package and qualifying the constant name as dates.DaysInWeek.

image

Nested package directories and import paths

When you’re working with the packages that come with Go, like fmt and strconv, the package name is usually the same as its import path (the string you use in an import statement to import the package). But as we saw in Chapter 2, that’s not always the case...

image

Some sets of packages are grouped together by import path prefixes like "archive/" and "math/". We said to think of these prefixes as being similar to the paths of directories on your hard drive...and that wasn’t a coincidence. These import path prefixes are created using directories!

image

You can nest groups of similar packages together in a directory in your Go workspace. That directory then becomes part of the import path for all the packages it contains.

Suppose, for example, that we wanted to add packages for greetings in additional languages. That would quickly become a mess if we placed them all directly in the src directory. But if we place the new packages under the greeting directory, they’ll all be grouped neatly together.

And placing the packages under the greeting directory affects their import path, too. If the dansk package were stored directly under src, its import path would be "dansk". But place it within the greeting directory, and its import path becomes "greeting/dansk". Move the deutsch package under the greeting directory, and its import path becomes "greeting/deutsch". The original greeting package will still be available at an import path of "greeting", as long as its source code file is stored directly under the greeting directory (not a subdirectory).

Suppose that we had a deutsch package nested under our greeting package directory, and that its code looked like this:

image

Let’s update our hi/main.go code to use the deutsch package as well. Since it’s nested under the greeting directory, we’ll need to use an import path of "greeting/deutsch". But once it’s imported, we’ll be using just the package name to refer to it: deutsch.

image

As before, we run our code by using the cd command to change to the src/hi directory within your workspace directory. Then, we use go run main.go to run the program. We’ll see the results of our calls to the deutsch package functions in the output.

image

Installing program executables with “go install”

When we use go run, Go has to compile the program, as well as all the packages it depends on, before it can execute it. And it throws that compiled code away when it’s done.

In Chapter 1, we showed you the go build command, which compiles and saves an executable binary file (a file you can execute even without Go installed) in the current directory. But using that too much risks littering your Go workspace with executables in random, inconvenient places.

The go install command also saves compiled binary versions of executable programs, but in a well-defined, easily accessible place: a bin directory in your Go workspace. Just give go install the name of a directory within src that contains code for an executable program (that is, .go files that begin with package main). The program will be compiled and an executable will be stored in this standard directory.

Note

(Be sure to pass the name of a directory within “src” to “go install”, not the name of a .go file! By default, “go install” isn’t set up to handle .go files directly.)

Let’s try installing an executable for our hi/main.go program. As before, from a terminal, we type go install, a space, and the name of a folder within our src directory (hi). Again, it doesn’t matter what directory you do this from; the go tool will look the directory up within the src directory.

image

When Go sees that the file inside the hi directory contains a package main declaration, it will know this is code for an executable program. It will compile an executable file, storing it in a directory named bin in the Go workspace. (The bin directory will be created automatically if it doesn’t already exist.)

Unlike the go build command, which names an executable after the .go file it’s based on, go install names an executable after the directory that contains the code. Since we compiled the contents of the hi directory, the executable will be named hi (or hi.exe on Windows).

image

Now, you can use the cd command to change to the bin directory within your Go workspace. Once you’re in bin, you can run the executable by typing ./hi (or hi.exe on Windows).

Note

You can also add your workspace’s “bin” directory to your system’s “PATH” environment variable. Then, you’ll be able to run executables in “bin” from anywhere on your system! Recent Go installers for Mac and Windows will update “PATH” for you.

Changing workspaces with the GOPATH environment variable

You may see developers on various websites talking about “setting your GOPATH” when discussing the Go workspace. GOPATH is an environment variable that Go tools consult to find the location of your workspace. Most Go developers keep all their code in a single workspace, and don’t change it from its default location. But if you want, you can use GOPATH to move your workspace to a different directory.

An environment variable lets you store and retrieve values, kind of like a Go variable, but it’s maintained by the operating system, not by Go. You can configure some programs by setting environment variables, and that includes the Go tool.

Suppose that, instead of in your home directory, you had set up your greeting package inside a directory named code in the root of your hard drive. And now you want to run your main.go file, which depends on greeting.

image

But you’re getting an error saying the greeting package can’t be found, because the go tool is still looking in the go directory in your home directory:

image

Setting GOPATH

If your code is stored in a directory other than the default, you’ll need to configure the go tool to look in the right place. You can do that by setting the GOPATH environment variable. How you’ll do that depends on your operating system.

On Mac or Linux systems:

You can use the export command to set the environment variable. At a terminal prompt, type:

export GOPATH="/code"

For a directory named code in the root of your hard drive, you’ll want to use a path of “/code”. You can substitute a different path if your code is in a different location.

On Windows systems:

You can use the set command to set the environment variable. At a command prompt, type:

set GOPATH="C:\code"

For a directory named code in the root of your hard drive, you’ll want to use a path of “C:\code”. You can substitute a different path if your code is in a different location.

Once that’s done, go run should immediately begin using the directory you specified as its workspace (as should other Go tools). That means the greeting library will be found, and the program will run!

image

Note that the methods above will only set GOPATH for the current terminal/command prompt window. You’ll need to set it again for each new window you open. But there are ways to set an environment variable permanently, if you want. The methods differ for each operating system, so we don’t have space to go into them here. If you type “environment variables” followed by the name of your OS into your favorite search engine, the results should include helpful instructions.

Publishing packages

We’re getting so much use out of our keyboard package, we wonder if others might find it useful, too.

image

Let’s create a repository to hold our code on GitHub, a popular code sharing website. That way, other developers can download it and use it in their own projects! Our GitHub username is headfirstgo, and we’ll name the repository keyboard, so its URL will be:

https://github.com/headfirstgo/keyboard

We’ll upload just the keyboard.go file to the repository, without nesting it inside any directories.

image
image

Hmm, that’s a valid concern. There can only be one keyboard directory in the Go workspace’s src directory, and so it looks like we can only have one package named keyboard!

image
image

Let’s try that: we’ll move our package into a directory structure that represents the URL where it’s hosted. Inside our src directory, we’ll create another directory named github.com. Inside that, we’ll create a directory named after the next segment of the URL, headfirstgo. And then we’ll move our keyboard package directory from the src directory into the headfirstgo directory.

Although moving the package into a new subdirectory will change its import path, it won’t change the package name. And since the package itself only contains references to the name, we don’t have to make any changes to the package code!

image

We will need to update the programs that rely on our package, though, because the package import path has changed. Because we named each subdirectory after part of the URL where the package is hosted, our new import path looks a lot like that URL:

"github.com/headfirstgo/keyboard"

We only need to update the import statement in each program. Because the package name is the same, references to the package in the rest of the code will be unchanged.

image
image

With those changes made, all the programs that rely on our keyboard package should resume working normally.

By the way, we wish we could take credit for this idea of using domain names and paths to ensure a package import path is unique, but we didn’t really come up with it. The Go community has been using this as a package naming standard from the beginning. And similar ideas have been used in languages like Java for decades now.

Downloading and installing packages with “go get”

Using a package’s hosting URL as an import path has another benefit. The go tool has another subcommand named go get that can automatically download and install packages for you.

We’ve set up a Git repository with the greeting package that we showed you previously at this URL:

https://github.com/headfirstgo/greeting

That means that from any computer with Go installed, you can type this in a terminal:

go get github.com/headfirstgo/greeting

Note

(Note: “go get” still may not be able to find Git after it’s installed. If this happens, try closing your old terminal or command prompt window and opening a new one.)

That’s go get followed by the repository URL, but with the “scheme” portion (the “https://”) left off. The go tool will connect to github.com, download the Git repository at the /headfirstgo/greeting path, and save it in your Go workspace’s src directory. (Note: if your system doesn’t have Git installed, you’ll be prompted to install it when you run the go get command. Just follow the instructions on your screen. The go get command can also work with Subversion, Mercurial, and Bazaar repositories.)

The go get command will automatically create whatever subdirectories are needed to set up the appropriate import path (a github.com directory, a headfirstgo directory, etc.). The packages saved in the src directory will look like this:

image

With the packages saved in the Go workspace, they’re ready for use in programs. You can use the greeting, dansk, and deutsch packages in a program with an import statement like this:

import (
       "github.com/headfirstgo/greeting"
       "github.com/headfirstgo/greeting/dansk"
       "github.com/headfirstgo/greeting/deutsch")

The go get command works for other packages, too. If you don’t already have the keyboard package we showed you previously, this command will install it:

go get github.com/headfirstgo/keyboard

In fact, the go get command works for any package that has been set up properly on a hosting service, no matter who the author is. All you’ll need to do is run go get and give it the package import path. The tool will look at the part of the path that corresponds to the host address, connect to that host, and download the package at the URL represented by the rest of the import path. It makes using other developers’ code really easy!

Reading package documentation with “go doc”

image

You can use the go doc command to display documentation on any package or function.

You can get a documentation for a package by passing its import path to go doc. For example, we can get info on the strconv package by running go doc strconv.

image

The output includes the package name and import path (which are one and the same in this case), a description of the package as a whole, and a list of all the functions the package exports.

You can also use go doc to get detailed info on specific functions by providing a function name following the package name. Suppose we saw the ParseFloat function in the list of the strconv package’s functions and we wanted to know more about it. We could bring up its documentation with go doc strconv ParseFloat.

You’ll get back a description of the function and what it does:

image

The first line looks just like a function declaration would look in code. It includes the function name, followed by parentheses containing the names and types of the parameters it takes (if any). If there are any return values, those will appear after the parameters.

This is followed by a detailed description of what the function does, along with any other information developers need in order to use it.

We can get documentation for our keyboard package in the same way, by providing its import path to go doc. Let’s see if there’s anything there that will help our would-be user. From a terminal, run:

go doc github.com/headfirstgo/keyboard

The go doc tool is able to derive basic information like the package name and import path from the code. But there’s no package description, so it’s not that helpful.

image

Requesting info on the GetFloat function doesn’t get us a description either:

image

Documenting your packages with doc comments

The go doc tool works hard to add useful info to its output based on examining the code. Package names and import paths are added for you. So are function names, parameters, and return types.

But go doc isn’t magic. If you want your users to see documentation of a package or function’s intent, you’ll need to add it yourself.

Fortunately, that’s easy to do: you simply add doc comments to your code. Ordinary Go comments that appear immediately before a package clause or function declaration are treated as doc comments, and will be displayed in go doc’s output.

Let’s try adding doc comments for the keyboard package. At the top of the keyboard.go file, immediately before the package line, we’ll add a comment describing what the package does. And immediately before the declaration of GetFloat, we’ll add a couple comment lines describing that function.

image

The next time we run go doc for the package, it will find the comment before the package line and convert it to a package description. And when we run go doc for the GetFloat function, we’ll see a description based on the comment lines we added above GetFloat’s declaration.

image

Being able to display documentation via go doc makes developers that install a package happy.

image

And doc comments make developers who work on a package’s code happy, too! They’re ordinary comments, so they’re easy to add. And you can easily refer to them while making changes to the code.

image

There are a few conventions to follow when adding doc comments:

  • Comments should be complete sentences.

  • Package comments should begin with “Package” followed by the package name:

    // Package mypackage enables widget management.
  • Function comments should begin with the name of the function they describe:

    // MyFunction converts widgets to gizmos.
  • You can include code examples in your comments by indenting them.

  • Other than indentation for code samples, don’t add extra punctuation characters for emphasis or formatting. Doc comments will be displayed as plain text, and should be formatted that way.

Viewing documentation in a web browser

If you’re more comfortable in a web browser than a terminal, there are other ways to view package documentation.

The simplest is to type the word “golang” followed by the name of the package you want into your favorite search engine. (“Golang” is commonly used for web searches regarding the Go language because “go” is too common a word to be useful for filtering out irrelevant results.) If we wanted documentation for the fmt package, we could search for “golang fmt”:

image

The results should include sites that offer Go documentation in HTML format. If you’re searching for a package in the Go standard library (like fmt), one of the top results will probably be from golang.org, a site run by the Go development team. The documentation will have much the same contents as the output of the go doc tool, with package names, import paths, and descriptions.

image

One major advantage of the HTML documentation is that each function name in the list of the package’s functions will be a handy clickable link leading to the function documentation.

image

But the content is just the same as what you’d see when running go doc in your terminal. It’s all based on the same simple doc comments in the code.

Serving HTML documentation to yourself with “godoc”

The same software that powers the golang.org site’s documentation section is actually available on your computer, too. It’s a tool called godoc (not to be confused with the go doc command), and it’s automatically installed along with Go. The godoc tool generates HTML documentation based on the code in your main Go installation and your workspace. It includes a web server that can share the resulting pages with browsers. (Don’t worry, with its default settings godoc won’t accept connections from any computer other than your own.)

image

To run godoc in web server mode, we’ll type the godoc command (again, don’t confuse that with go doc) in a terminal, followed by a special option: -http=:6060.

Then with godoc running, you can type the URL:

http://localhost:6060/pkg

...into your web browser’s address bar and press Enter. Your browser will connect to your own computer, and the godoc server will respond with an HTML page. You’ll be presented with a list of all the packages installed on your machine.

image

Each package name in the list is a link to that package’s documentation. Click it, and you’ll see the same package docs that you’d see on golang.org.

image

The “godoc” server includes YOUR packages!

If we scroll further through our local godoc server’s list of packages, we’ll see something interesting: our keyboard package!

image

In addition to packages from Go’s standard library, the godoc tool also builds HTML documentation for any packages in your Go workspace. These could be third-party packages you’ve installed, or packages you’ve written yourself.

Click the keyboard link, and you’ll be taken to the package’s documentation. The docs will include any doc comments from our code!

image

When you’re ready to stop the godoc server, return to your terminal window, then hold the Ctrl key and press C. You’ll be returned to your system prompt.

image

Go makes it easy to document your packages, which makes packages easier to share, which in turn makes them easier for other developers to use. It’s just one more feature that makes packages a great way to share code!

Your Go Toolbox

image

That’s it for Chapter 4! You’ve added packages to your toolbox.

image

Pool Puzzle Solution

Your job is to take code snippets from the pool and place them into the blank lines. Don’t use the same snippet more than once, and you won’t need to use all the snippets. Your goal is to set up a calc package within a Go workspace so calc’s functions can be used within main.go.

image

Get Head First Go now with O’Reilly online learning.

O’Reilly members experience live online training, plus books, videos, and digital content from 200+ publishers.