Skip to content

proposal: generic should infer type from variable definition #50285

Open
@LeGamerDc

Description

@LeGamerDc

hope go could support infer generic type from variable type

func main() {
	var x Person = get() // to replace var x = get[Person]()
	fmt.Println(x)
}

func get[T any]() T {
	var x T
	return x
}

Activity

added this to the Proposal milestone on Dec 21, 2021
ianlancetaylor

ianlancetaylor commented on Dec 21, 2021

@ianlancetaylor
Contributor

I suppose this would be a little bit like way that the language handles untyped constants, in that the type of the function would be inferred based on how the function call expression is used. But there would be a big difference, which is that untyped constants have a default type, but here the "default type" would be a compiler error.

I'm concerned that the rules for when the type is inferred would be unclear to the person reading the program. People already get confused about untyped constants.

In any case putting this on hold for later consideration after we have more experience with generics.

leaxoy

leaxoy commented on Dec 24, 2021

@leaxoy

And generic parameter should can be infer lazily.
For example:

m := NewHashMap[int, int]() 
m.Put(1, 2)

can write as:

m := NewHashMap() // currently type parameter is not set.
m.Put(1, 2) // type parameter set to (int, int)
ianlancetaylor

ianlancetaylor commented on Dec 24, 2021

@ianlancetaylor
Contributor

@leaxoy That seems very different to me, and should be a separate issue, not this one. Thanks.

mlevieux

mlevieux commented on Jan 3, 2022

@mlevieux
Contributor

While this is on hold, I'd like to give my two cents on this one:
I don't want type inference to get too smart, and IMO nobody should wish for this. When we'll start having big projects using generics, or complex libraries with functions having a certain number of type parameters, differing in subtle ways, interacting with each other etc... it'll become more and more difficult to read generic code, up to the point where (and that is already the case in a number of languages) it'll be necessary to know a tremendous part of the codebase to know what types you're actually dealing with, their constraints and such. So I left a thumb down, not because I think this is bad in itself, I just feel like it goes against Go's philosophy.

changkun

changkun commented on Apr 17, 2022

@changkun
Member

For the following g and f:

func f[T any]() (x T) { return }
func g[T any](x ...T) { return }

On the call side, because the current type inference is only limited based on function arguments, we can infer g from the return type of f:

g(f[int]()) // OK

But not the vise versa:

g[int](f()) // ERROR: cannot infer T

This seems OK initially since it applies the philosophy of "there is only one way of doing something". However, whenever we trying to do something with ..., it feels so much wierd (am I the only person?):

g(f[int](), f[int](), f[int](), f[int](), f[int]()) // OK
g[int](f(), f(), f(), f(), f())                     // ERROR: cannot infer T

One must write the first style rather than the second. Was there a particular example (similar to other known limitations caused by particular examples) to prevent this during the design cycle of 1.18's generics?

moved this to Hold in Proposalson Aug 10, 2022

14 remaining items

fzipp

fzipp commented on Mar 31, 2023

@fzipp
Contributor

z, v := g[string] // ok, P = int (provided), z is int, Q = float64, inferred from v

I guess that should be z, v := g[int].

griesemer

griesemer commented on Mar 31, 2023

@griesemer
Contributor

@DeedleFake For now I think we may want to constrain this to a limited set of "assignments". Once we add sending results to a channel, we (probably) also need to add setting a map element or providing a map key. Then the question is what about indexing (do we infer an int?), etc. In the cases of channels and maps we have clearly defined types and so it makes sense to include those, but let's do this in a separate round, ounce we are happy with this in the first place.

griesemer

griesemer commented on May 16, 2023

@griesemer
Contributor

The primary issue with this proposal is that in general, type-inference is not easily localized anymore to a single ("flat") assignment/function call. Consider:

func f[P any](x P) P { return x }

var x float64 = f(f(f(0)))

The return type P of the outermost call of f is inferred from the assignment, i.e., P is float. That informs f's argument type, which in turn determines the result type and argument type of the middle f call, and then the innermost f call. The type of 0 will then be inferred to be float64.

In general, this nesting can be arbitrarily deep, with many different generic functions at various instantiation levels. While type inference should be able to infer types in cases like these, our current type checkers are not organized in a way that makes such an approach easily feasible: type checking essentially happens recursively, bottom-up. To make this more general inference work, expressions will need to be type-checked "as a whole" which likely will require significant re-engineering of the type checker.

Putting back on proposal-hold for now.

LeGamerDc

LeGamerDc commented on Jan 14, 2024

@LeGamerDc
Author

[#58650] solved the problem, should close now

zigo101

zigo101 commented on Jan 14, 2024

@zigo101

Solved?

pat42smith

pat42smith commented on Jan 14, 2024

@pat42smith

Is this issue really "solved"? My understanding was that #58650 does not implement this; it just makes this easier to implement.

Also, a small modification of the code from the top of this issue does not compile in the playground, not even with the version set to "Go dev branch". https://go.dev/play/p/ry-w07CKoct?v=gotip

griesemer

griesemer commented on Jan 16, 2024

@griesemer
Contributor

This issue is not solved. See this comment for details. Leaving on hold.

kkqy

kkqy commented on Jun 7, 2024

@kkqy

I meet the same problem.
I use this pattern to pass variable options.

type Container[T any] struct {
	name  string
	items map[*T]struct{}

	a interface{}
	b interface{}
	c interface{}
}

func (c *Container[T]) Add(item *T) { c.items[item] = struct{}{} }

// ... and so on

// I use type ContainerOption configure the Container.
type ContainerOption[T any] func(*Container[T])

func WithName[T any](name string) ContainerOption[T] { return func(c *Container[T]) { c.name = name } }
func WithA[T any](a any) ContainerOption[T]          { return func(c *Container[T]) { c.a = a } }
func WithB[T any](b any) ContainerOption[T]          { return func(c *Container[T]) { c.b = b } }
func WithC[T any](c any) ContainerOption[T]          { return func(c *Container[T]) { c.c = c } }

func NewContainer[T any](opts ...ContainerOption[T]) bool {
	c := &Container[T]{
		items: make(map[*T]struct{}),
	}
	for _, opt := range opts {
		opt(c)
	}
	return true
}

func main() {
	// the "int" is not meaningful for this statement and it looks very strange.
	NewContainer(WithName[int]("TestContainer"), WithA[int]("bar"), WithB[int]("foo"), WithC[int]("baz"))

	// why not:
	// NewContainer[int](WithName("TestContainer"), WithA("bar"), WithB("foo"), WithC("baz"))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Hold

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @dominikh@rogpeppe@josharian@DeedleFake@jimmyfrasche

        Issue actions

          proposal: generic should infer type from variable definition · Issue #50285 · golang/go