As part of preparing for my dotGo talk I updated a few of my packages to use the functional options pattern. I only had time to show one of those packages on stage, pkg/term. This is a post about one that was left on the cutting room floor.
Last year I wrote a simple package to help me automate profiling of my programs. It used the Config struct approach for configuration. Here is an example from the existing documentation.
package main
import "github.com/davecheney/profile"
func main() {
config := profile.Config{
CPUProfile: true,
MemProfile: false,
BlockProfile: false,
}
defer profile.Start(&config).Stop()
// ...
}
Not long after I released the package, I started to get reports from users who had tried to turn on all the profiling options at the same time; they were finding the profiles interfered with each other.
I didn’t want to complicate the API with some sort of validation of the configuration, possibly returning an error. It was important to me to retain the single line usage of the package.
So, I settled for providing sample Config
values that enabled just one profile option at a time, and documented that turning on multiple profiles at a time was a bad thing.
I also changed the signature of the function to take a *Config
and arranged that if the caller passed nil
they would get CPU profiling.
In preparing for the talk I decided to revisit my profiling package, and by applying functional options I think I have improved the API.
package main
import "github.com/pkg/profile"
func main() {
// CPU profiling by default
defer profile.Start().Stop()
// ...
}
Now we always enable CPU profiling unless you choose a different profile, which is done explicitly.
defer profile.Start(profile.MemProfile).Stop()
Previously if you specified more than one profile you got a corrupted result, and specifying no profiles, ie passing an empty Config,
produced no profile at all. Now that’s all fixed.
I was also delighted to discover that as the option functions are now real functions, you can hang examples off them. Take a look at the example of the NoShutdownHook
option.
Sourcegraph tells me that several projects are already using the old version of this package, so to avoid breaking those, I have pushed the updated version to a new repository, github.com/pkg/profile.
The old package should be considered deprecated, and you should update your code to use the new version, it’s bloody easy.