Hello.
Today’s will be a quick post. Everyone knows and loves/hates functional options1 in Go.
The biggest gripe people get with it is, that the options aren’t discoverable and that there is no IDE support for nicely auto-completing options.
My thought about this was that, what if we would just hang it on a struct? Let’s see how that looks.
Consider this normal server builder with options:
type Server struct {
Name string
Address string
Port int
}
func WithName(name string) ServerOptFn {
return func(s *Server) {
s.Name = name
}
}
func WithAddress(address string) ServerOptFn {
return func(s *Server) {
s.Address = address
}
}
func WithPort(port int) ServerOptFn {
return func(s *Server) {
s.Port = port
}
}
type ServerOptFn func(*Server)
func NewServer(opts ...ServerOptFn) *Server {
s := &Server{}
for _, o := range opts {
o(s)
}
return s
}
Now, what if you would like to retain the niceness of the clean options pattern where you don’t have to specify and empty struct but still could use a struct to gather the options together?
type Server struct {
Name string
Address string
Port int
}
type ServerOpts struct{}
func (ServerOpts) WithName(name string) ServerOptFn {
return func(s *Server) {
s.Name = name
}
}
func (ServerOpts) WithAddress(address string) ServerOptFn {
return func(s *Server) {
s.Address = address
}
}
func (ServerOpts) WithPort(port int) ServerOptFn {
return func(s *Server) {
s.Port = port
}
}
type ServerOptFn func(*Server)
Now, all the options are on the ServerOpts
struct.
And then calling the builder would be something like this:
server := pkg.NewServer(
pkg.ServerOpts{}.WithAddress("address"),
pkg.ServerOpts{}.WithName("name"),
pkg.ServerOpts{}.WithPort(9998))
server.Start()
To make it nicer, create a variable:
serverOpts := pkg.ServerOpts{}
server := pkg.NewServer(
serverOpts.WithAddress("address"),
serverOpts.WithName("name"),
serverOpts.WithPort(9998))
server.Start()
That’s not half that bad, I recon. And looking at the docs, we can see that the options are nicely put on the ServerOpts struct therefore they are discoverable.
Conclusion
What do you think? Yay or nay?
Thanks for reading!