Skip to content
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

proposal: spec: apply constraint type inference to generic types #62460

Open
itsmontoya opened this issue Sep 5, 2023 · 12 comments
Open

proposal: spec: apply constraint type inference to generic types #62460

itsmontoya opened this issue Sep 5, 2023 · 12 comments
Labels
Proposal Proposal-Hold TypeInference Issue is related to generic type inference
Milestone

Comments

@itsmontoya
Copy link

itsmontoya commented Sep 5, 2023

Author background

  • Experienced Go Engineer, over 8 years experience
  • Rust, C, Zig

Related proposals

  • I am not sure if this idea has been proposed before
  • This idea does not effect error handling
  • This proposal is about generics

Proposal

  • The proposed change is to allow for better type inference when working with generic referencing interfaces
  • This proposal helps anyone who uses generics in a factory or generator pattern. Additionally, anyone writing data structures
  • The purpose of this proposal is to allow for interface types referencing generics type MyInterface[T] interface { *T } to be inferred better
  • The ability to infer types for interfaces targeting a pointer value of a generic type
  • Allow for pointer types of structs to be inferred more easily by Golang generics
  • The change is backward compatible
  • This change does not affect existing features
  • This change is not a performance improvement

Costs

  • This would not affect the learning scale of Go as it's a deeper subject. For more experienced Engineers, it should make generics more simple to work with
  • More work on the compiler to perform stronger inferring
  • gopls would be affected
  • Negligible, but a slight performance cost
  • No runtime cost
  • A use-case for this feature is available here
  • No prototype is available for the implementation of this feature

Example - Current Requirement

package main

import (
	"strconv"
)

func main() {
	c := New[testType]()
	c.Txn(func(txn Txn[testType, *testType]) {
		txn.New(testType{val: "1"})
		txn.Update("0", func(tt testType) (out testType) {
			return
		})
	})
}

func New[T any, U Value[T]]() *Controller[T, U] {
	var c Controller[T, U]
	c.m = make(map[string]T)
	return &c
}

type Controller[T any, U Value[T]] struct {
	idx int
	m   map[string]T
}

func (c *Controller[T, U]) Txn(fn func(Txn[T, U])) {
	var txn Txn[T, U]
	txn.c = c
	fn(txn)
}

type Txn[T any, U Value[T]] struct {
	c *Controller[T, U]
}

func (txn *Txn[T, U]) New(t T) {
	u := U(&t)
	u.SetID(strconv.Itoa(txn.c.idx))
	txn.c.idx++
}

func (txn *Txn[T, U]) Update(key string, fn func(T) T) {
	v, ok := txn.c.m[key]
	if !ok {
		return
	}

	txn.c.m[key] = fn(v)
}

type testType struct {
	id  string
	val string
}

func (t *testType) SetID(id string) {
	t.id = id
}

type Value[T any] interface {
	*T
	SetID(string)
}

Example - Proposal

package main

import (
	"strconv"
)

func main() {
	c := New[testType]()
	c.Txn(func(txn Txn[testType]) {
		txn.New(testType{val: "1"})
		txn.Update("0", func(tt testType) (out testType) {
			return
		})
	})
}

func New[T any, U Value[T]]() *Controller[T, U] {
	var c Controller[T, U]
	c.m = make(map[string]T)
	return &c
}

type Controller[T any, U Value[T]] struct {
	idx int
	m   map[string]T
}

func (c *Controller[T, U]) Txn(fn func(Txn[T, U])) {
	var txn Txn[T, U]
	txn.c = c
	fn(txn)
}

type Txn[T any, U Value[T]] struct {
	c *Controller[T, U]
}

func (txn *Txn[T, U]) New(t T) {
	u := U(&t)
	u.SetID(strconv.Itoa(txn.c.idx))
	txn.c.idx++
}

func (txn *Txn[T, U]) Update(key string, fn func(T) T) {
	v, ok := txn.c.m[key]
	if !ok {
		return
	}

	txn.c.m[key] = fn(v)
}

type testType struct {
	id  string
	val string
}

func (t *testType) SetID(id string) {
	t.id = id
}

type Value[T any] interface {
	*T
	SetID(string)
}
@itsmontoya itsmontoya added LanguageChange Proposal v2 A language change or incompatible library change labels Sep 5, 2023
@gopherbot gopherbot added this to the Proposal milestone Sep 5, 2023
@DeedleFake
Copy link

There's an awful lot of what looks, to me at least, like extraneous information in those code examples that are kind of obscuring the actual change. The diff between the two is just

@@ -6,7 +6,7 @@
 
 func main() {
 	c := New[testType]()
-	c.Txn(func(txn Txn[testType, *testType]) {
+	c.Txn(func(txn Txn[testType]) {
 		txn.New(testType{val: "1"})
 		txn.Update("0", func(tt testType) (out testType) {
 			return

@deltamualpha
Copy link

And as far as I can tell that's only possible in the specific case [T any, U Value[T]] -- "The ability to infer types for interfaces targeting a pointer value of a generic type"?

@tdakkota
Copy link

tdakkota commented Sep 5, 2023

It seems this issue is about redundant parameter type declaration in closures.
See #21498.

@DeedleFake
Copy link

It seems this issue is about redundant parameter type declaration in closures.
See #21498.

I'm not sure about that. I think it's actually related to a specific case of structural constraints.

@ianlancetaylor ianlancetaylor added TypeInference Issue is related to generic type inference and removed LanguageChange v2 A language change or incompatible library change labels Sep 5, 2023
@ianlancetaylor
Copy link
Contributor

Can you describe the new type inference that you are looking for using the kind of language as issue #58650?

Can you provide a minimal example?

Thanks.

@seankhliao seankhliao added the WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. label Sep 5, 2023
@itsmontoya
Copy link
Author

There's an awful lot of what looks, to me at least, like extraneous information in those code examples that are kind of obscuring the actual change. The diff between the two is just

@@ -6,7 +6,7 @@
 
 func main() {
 	c := New[testType]()
-	c.Txn(func(txn Txn[testType, *testType]) {
+	c.Txn(func(txn Txn[testType]) {
 		txn.New(testType{val: "1"})
 		txn.Update("0", func(tt testType) (out testType) {
 			return

You're 100% correct. Sorry about that! Thank you for clarifying the changes

@itsmontoya
Copy link
Author

itsmontoya commented Sep 5, 2023

Can you describe the new type inference that you are looking for using the kind of language as issue #58650?

Can you provide a minimal example?

Thanks.

It looks as though my explained issue would be solved by #58650 (Constraint type inference 2)

@ianlancetaylor
Copy link
Contributor

The "Constraint type inference 2" example already works, though, so there is some essential difference in this proposal.

@Merovius
Copy link
Contributor

Merovius commented Sep 6, 2023

I think the essential difference - based on the diff - is that this proposal is about applying constraint type inference to generic types, not just generic function calls. That is, the goal is to make this work:

package main

func main() {
	var x X[*S]
	_ = x
}

type C[T any] interface {
	*T
	M()
}

type X[PT C[T], T any] struct {
	v T
}

func (x *X[PT, T]) N() {
	PT(&x.v).M()
}

type S struct{}

func (*S) M() {}

This proposal specifically asks about a situation where the type appears as the argument type in a function-literal passed to a function call. But I think the main hurdle is still, that we don't even try to infer type-arguments for generic types.

@itsmontoya
Copy link
Author

@Merovius you make some fantastic points and you are 💯 correct. I appreciate you noticing the subtle differences that I missed.

@ianlancetaylor ianlancetaylor removed the WaitingForInfo Issue is not actionable because of missing required information, which needs to be provided. label Sep 7, 2023
@ianlancetaylor
Copy link
Contributor

CC @griesemer

@seankhliao seankhliao changed the title proposal: generics: improve inferring of interface-type reference proposal: spec: apply constraint type inference to generic types Sep 10, 2023
@griesemer
Copy link
Contributor

@itsmontoya We're not doing any inference (specifically "constraint type inference") when instantiating generic types for now because of issues with type cycles that we don't fully understand: in contrast to generic functions, a generic type may occur in its own type parameter list (e.g., as constraint or part of a constraint), directly or indirectly. The resulting cycles lead to difficult problems. We probably could detect such (for now) invalid cycles, but that also requires some theory behind it; and we haven't had time to look into this.

We may do this at some point but nothing is planned in the near future. Putting on hold for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Proposal Proposal-Hold TypeInference Issue is related to generic type inference
Projects
Status: Hold
Development

No branches or pull requests

9 participants