July 20th, 2024

Dysfunctional Options Pattern in Go

The Functional Options Pattern in Go is compared to the Dysfunctional Options Pattern for configuring APIs. The latter, resembling the builder pattern, offers a simpler and faster alternative with method chaining.

Read original articleLink Icon
Dysfunctional Options Pattern in Go

The article discusses the Functional Options Pattern in Go, highlighting its benefits and drawbacks in configuring APIs. It explains how the pattern works, the challenges it poses in maintaining public APIs, and the need for workarounds due to Go's lack of default function arguments. The Dysfunctional Options Pattern is introduced as a simplified alternative, utilizing method chaining for configuring optional attributes without excessive indirection. A comparison between the two patterns shows the Dysfunctional Options Pattern to be significantly faster. The article concludes by mentioning the similarity of the Dysfunctional Options Pattern to the builder pattern in object-oriented languages, emphasizing its lightweight nature and efficiency. The Dysfunctional Options Pattern offers a more straightforward approach to configuring APIs in Go, addressing some of the complexities associated with the Functional Options Pattern.

Link Icon 5 comments
By @ivanjermakov - 3 months
The problem with options in non-ML languages is that invalid state is unrepresentable[1]. E.g, in Java Option<Integer> can either be null, empty, some with null and some with a number.

To solve this problem, a language have to disallow null/nil as a bottom type and provide nice syntax to initialize types with optional types.

[1]: https://geeklaunch.io/blog/make-invalid-states-unrepresentab...

By @evanmoran - 3 months
I like this write up. Nicely done! One think I have enjoyed doing is using named parameters as the options, but it does require a second struct only for the option parts:

    NewObject(
      required1, 
      required2, 
      &ObjectOptions{
        Option1: “foo”,
        // Option2 skipped
        Option3: “bar”,
    })
I can see the merits of both (two structs is tedious, for sure), but I wanted to share in case others had thoughts or alternate ideas!
By @abahlo - 3 months
Isn’t this just the builder pattern without Build()?
By @iambvk - 3 months
What are the downsides of just adding new methods on the `Config` structure?

    func (c *Config) WithFizz(...) {...}

    func (c *Config) WithBazz(...) {...}
By @tail_exchange - 3 months
Looks cleaner and simpler. I like this pattern!