[ANN] optioner: Functional Options Generator

222 views
Skip to first unread message

Leo Neumeyer

unread,
Dec 29, 2014, 2:13:49 PM12/29/14
to golan...@googlegroups.com
In a typical day I run experiments that use data and algorithms that require adjusting parameters and running on multiple hosts. I want an elegant solution to set options in a Go package. I recently started to experiment with functional options [1,2] and found the approach clean and simple, especially when most options use default values. The issue, of course, was that I had to write  boilerplate code which is not too bad but I'd rather not do it. Inspired by the stringer command, I decided to try "go generate" to generate boilerplate for functional options. The result can be found here:


For a package with:

package example

//go:generate optioner -type Example
type Example struct {
   N int
   F funt(int)int
}

The package user only needs to do this:

ex := example.NewExample(example.N(22), example.F(myFunc))


Warning: I use the ast package for the first time over the weekend and I'm sure this can be done in a more elegant way! Also, this tool may not be necessary in many cases, for example, Martin Bruse suggested a simpler solution using anonymous functions:

ex := example.NewExample("test", func(ex *Example) {
  ex.N = 22
  ex.F = myFunc
})

as far as I can tell this is a good solution for many use cases except when default values are required which will also require boilerplate. 

what do you think? any other ideas?


Martin Bruse

unread,
Dec 29, 2014, 4:24:34 PM12/29/14
to golan...@googlegroups.com
You could set all fields to default values in the constructor func, before you run your func options, then you would get defaults except when explicitly overriding them.

Of course, to modify non exported fields you would still need boiler plate.

A way to avoid exporting all fields, or writing boiler plate for the non exported, would be something like

type Service struct {
config Config

}

type Config struct {
Option1 string
Option2 int
}

func New(fs ...func(*Config) error) (result *Service, err error) {
result = &Service{
config: defaultConfig(),
}
for _, f := range fs {
if err = f(&result.config); err != nil {
return
}
}
return
}

This way you could make all config fields available only during construction, and hide them afterwards to avoid misunderstandings.

This way at least some of the boiler plate problems you mention would be avoided.

Just thinking out loud here, so forgive me if I misunderstand :)

Val

unread,
Dec 29, 2014, 5:06:00 PM12/29/14
to golan...@googlegroups.com
Hi Leo
I like the idea, here are my remarks :
- it is cool for standard (boring) lists of "setters" with no extra logic.  It reminds of Named parameters, or setter chaining like example.NewExample().SetN(22).SetF(myFunc)
- but Functional options are most powerful with extra logic added in each option. This of course must be wriiten by the developer, not generated.
- how about having Option return an option that rollbacks the whole variadic list?  Would be nice :)

Best regards
Valentin

Leo Neumeyer

unread,
Dec 29, 2014, 7:06:07 PM12/29/14
to Val, golang-nuts
Thanks for the feedback. Yes, for the options that needs extra functionality, the package author will exclude the field and implement manually. At least in my case, most options are boilerplate.

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/x8jmpCbI5Aw/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--
Reply all
Reply to author
Forward
0 new messages