Skip to content

proposal: generics inference improvement #50659

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
thomascoquet opened this issue Jan 17, 2022 · 1 comment
Closed

proposal: generics inference improvement #50659

thomascoquet opened this issue Jan 17, 2022 · 1 comment

Comments

@thomascoquet
Copy link

I would like to suggest a pattern for composable functional options using go generics. This pattern is useful in the case where functional options are reused in several functions/methods, with subtle differences.

In order to make it work in a user-friendly way, inference would need to be modified/augmented for generics.

Rational

When dealing with large libraries, one often reuse functional options that are applicable to several methods/functions.

This generates a lot of boilerplate code as one method needs to be added for each and everyone of the option structs.
An example of this pattern (taken from https://sagikazarmark.hu/blog/functional-options-on-steroids/):

type ClientOption interface {
	setClientOption(*clientOpts)
}

type ServerOption interface {
	setServerOption(*serverOpts)
}

func WithLogger(logger Logger) interface {
    ClientOption
    ServerOption
} {
    return wl{logger}
}

type wl struct {
    logger Logger
}

func (o wl) setServerOption(s *serverOpts) {
    s.logger = o.logger
}

func (o wl) setClientOption(c *clientOpts) {
    c.logger = o.logger
}

A larger version of such a pattern: https://github.com/airbusgeo/godal/blob/main/options.go

Proposal

Use generics to create composable options and decrease boiler plate code.
Complete version in playground: https://go.dev/play/p/HS_CIFTUtqk?v=gotip

package main

import "fmt"

func WithPort[T interface{ setPort(int) }](p int) func(T) {
	return func(f T) {
		f.setPort(p)
	}
}

type portOpt struct {
	port int
}

func (po portOpt) Port() int {
	return po.port
}

func (po *portOpt) setPort(v int) {
	po.port = v
}

// see playground for other examples

type myOption struct {
	portOpt
	flagOpt
}

type MyOption = *myOption

func Apply(optfns ...func(MyOption)) {
	opts := myOption{}
	for _, optfn := range optfns {
		optfn(&opts)
	}
	fmt.Println("port", opts.Port())
	fmt.Println("flag", opts.Flag())
}

func main() {
	Apply(WithPort[MyOption](15), WithFlag[MyOption]())
}

Unfortunately, the inference system is not capable to infer the type of options to use what makes mandatory to instantiate WithPort using the type MyOption:

Apply(WithPort(15), WithFlag())

returns:

./prog.go:82:16: cannot infer T (prog.go:27:15)

Similarly, the following does not work either:

var opt func(MyOption) = WithPort(15)

whereas the following work:

var opt func(MyOption) = WithPort[MyOption](15)

Would such a change in the inference be possible?

Thanks, Thomas

@gopherbot gopherbot added this to the Proposal milestone Jan 17, 2022
@thomascoquet thomascoquet changed the title proposal: generics inference for proposal: generics inference improvement Jan 17, 2022
@seankhliao
Copy link
Member

Duplicate of #47868, #50285

@golang golang locked and limited conversation to collaborators Jan 17, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants