Using generics to solve Optional argument problem for multiple methods

453 views
Skip to first unread message

John

unread,
Jul 27, 2022, 5:54:20 PM7/27/22
to golang-nuts
With 1.18 generics, I'm curious if anyone has a good solution using generics to solve the optional argument that can be used in multiple method problem.

So say I have two methods, .A() and .B().  Both accept optional arguments where some optional arguments might be shared.

Here is a very loose (do not take any of this as the solution, just trying to show what I'm kinda trying to achieve). 

type AOptions struct{
  Shared Opt string
  NotSharedOpt bool
}

type BOptions struct{
  Shared Opt string
  NotSharedOpt int
}

func WithShared(shared string) CallOption {
  ...
}

func WithANotShared(b bool) CallOption {
  ...
}

func (c Client) A(options ...CallOption) {
  opts := AOptions{}
  for _, o := range options {
    o(&opts)
  }
  ... 
}
func (c Client B(options ...CalOption) {
  ...
}

We want to be able to use WithShared() with both A() and B(), but only  WithANotShared with the A() method.  

I've had different solutions to this problem that would give runtime errors, but I'm curious with generics if there is one for compile time that anyone has come up with.

Cheers.

John

unread,
Jul 27, 2022, 7:12:16 PM7/27/22
to golang-nuts
As a note, this is how I solve this problem without generics:

Henry

unread,
Jul 28, 2022, 12:44:15 AM7/28/22
to golang-nuts
```
type WithSharedOption interface {
   SomeMethod()
}

type OptionA struct {
}

func (a OptionA) SomeMethod(){}

type OptionB struct {
}

func (b OptionB) SomeMethod(){}

//without generics
func WithShared(option WithSharedOption){}
func WithANotShared(option OptionA){}

//with generics
func WithShared[T WithSharedOption](option T){}
func WithANotShared(option OptionA){}
```

Andrew Harris

unread,
Jul 28, 2022, 3:57:50 PM7/28/22
to golang-nuts
There's a trivial conversion from any shared option to a flavored optionFunc[T]:

type optionFunc[T any] func(*client)

func reflavor[T any]( shared func(*client)) optionFunc[T] {
    return optionFunc[T](shared)
}

I'm not sure exactly where reflavoring shared -> optionFunc[T] would fit in best, my first approach might be that the optionStructA and optionStructB could return appropriately flavored slices []optionFunc[A], []optionFunc[B].

Then, the A(), B() methods can accept only the right option flavor:

func (c *client) A( ...option[A])
func (c *client) B( ...option[B])

John

unread,
Jul 28, 2022, 5:28:15 PM7/28/22
to golang-nuts
Thanks Henry and Harry.

This helped expose me to some other options. The only thing about these is they don't particularly look user friendly (which I didn't ask for).  I've streamlined my version (non-generic) out to this:
https://github.com/johnsiilver/calloptions . The godoc has an explanation on how to use it.  

It gives type safety and limits which calls can be used.  It also looks exactly to the user like the normal functional arguments. It only has two problems (that I see):
  • It is complicated to implement, a newbie maintainer of some package is going to have no idea what's happening
  • There is a single any in that gets passed to the Do() method
This any should be fine in this case, because you'd have to add support for a method but not implement it for it to be a runtime problem.  But it still irks me that I can't get it to a compile time check.

Thank you both for being generous with your time.

Cheers!
Reply all
Reply to author
Forward
0 new messages