Skip to content

spec: type inference doesn't permit inferring interface type parameter from argument #50484

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
fakke opened this issue Jan 6, 2022 · 6 comments
Labels
FrozenDueToAge generics Issue is related to generics NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Milestone

Comments

@fakke
Copy link

fakke commented Jan 6, 2022

What version of Go are you using (go version)?

$ go version
go version go1.18beta1 darwin/amd64

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="~/Library/Caches/go-build"
GOENV="~/Library/Application Support/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOINSECURE=""
GOMODCACHE="~/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="darwin"
GOPATH="~/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="~/sdk/go1.18beta1"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="~/sdk/go1.18beta1/pkg/tool/darwin_amd64"
GOVCS=""
GOVERSION="go1.18beta1"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="clang"
CXX="clang++"
CGO_ENABLED="1"
GOMOD=
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -arch x86_64 -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/m4/5y8kv34x7hn27crxq3cgwmrh0000gn/T/go-build3708845374=/tmp/go-build -gno-record-gcc-switches -fno-common"

What did you do?

type CanQuack[Q any] interface {
	Quack() Q
}

type Duck[Q any] struct {
	quack Q
}

func (d Duck[Q]) Quack() Q {
	return d.quack
}

func ForceQuack[Q any](quacker CanQuack[Q]) {
	print(quacker.Quack())
}

func main() {
	duck := Duck[string]{quack: "great success"} // redundant type specification forced by #50482
	ForceQuack(duck)                             // still, compiler cannot infer Q
}

What did you expect to see?

great success

What did you see instead?

type Duck[string] of duck does not match CanQuack[Q] (cannot infer Q)

@ianlancetaylor ianlancetaylor added generics Issue is related to generics NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one. labels Jan 7, 2022
@ianlancetaylor ianlancetaylor added this to the Backlog milestone Jan 7, 2022
@ianlancetaylor
Copy link
Contributor

This code has a generic interface type, and uses that type as a function parameter. You are suggesting that type inference should infer the type argument for the generic interface type based on the type being used to call the function. There is no type inference that supports that at present. In general we are being very cautious about type inference. It's essential that nobody ever be surprised by an inferred type.

This isn't going to change for 1.18. We can consider this kind of type inference for a later release.

@ianlancetaylor ianlancetaylor changed the title type inference doesn't work with generic duck typing spec: type inference doesn't permit inferring interface type parameter from argument Jan 7, 2022
@beoran
Copy link

beoran commented Jan 7, 2022

The easiest workaround for this is to add a Make function, then inference will work correctly, like this:

https://gotipplay.golang.org/p/kD0BP_bfQDa

`package main

import (
"fmt"
)

// This playground uses a development build of Go:
// devel go1.18-da7891f6f3 Thu Jan 6 15:00:16 2022 +0000

type Quacker[Q any] interface {
Quack()
}

type Duck[Q any] struct {
sound Q
}

func MakeDuckQuacker[Q any](sound Q) Quacker[Q] {
return Duck[Q]{sound: sound}
}

func (d Duck[Q]) Quack() {
fmt.Printf("%v", d.sound)
}

func ForceQuack[Q any](quacker Quacker[Q]) {
quacker.Quack()
}

func main() {
duck := MakeDuckQuacker("great success")
ForceQuack(duck)
}
`

@fakke
Copy link
Author

fakke commented Jan 9, 2022

The easiest workaround for this is to add a Make function, then inference will work correctly, like this:

unfortunately, this workaround doesn't solve practical cases where objects implement more than one interface.
even for that reason alone, it only makes sense to return the most general interface from a Make function.
the Duck is also a Walker, i will have to work around the workaround to pass the object created with MakeDuckQuacker to a ForceWalk(Walker) function.

@fakke
Copy link
Author

fakke commented Jan 9, 2022

You are suggesting that type inference should infer the type argument for the generic interface type based on the type being used to call the function.

isn't that the whole point of having interfaces? if an object implements an interface, it should be usable wherever that interface is accepted

This isn't going to change for 1.18. We can consider this kind of type inference for a later release.

i'm really looking forward to that. this is one of the 3 key blockers to having proper generic collections in go.
the other two are having generic methods in interfaces (to implement Map) and recursive generic types (to allow chaining the calls properly)
and having haskell-style lambdas would make the code so much less bloated... but i'm dreaming :)

@ianlancetaylor
Copy link
Contributor

isn't that the whole point of having interfaces? if an object implements an interface, it should be usable wherever that interface is accepted

Yes, but as far as I can tell that's not what this issue is about. This issue is about whether we should infer the type argument for a parameterized interface type. It's not about the interface type, it's about inferring the type arguments. You can always write the type arguments explicitly yourself.

@rogpeppe
Copy link
Contributor

This is a duplicate of #41176 AFAICS, so closing as such.

@golang golang locked and limited conversation to collaborators Jan 30, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
FrozenDueToAge generics Issue is related to generics NeedsInvestigation Someone must examine and confirm this is a valid issue and not a duplicate of an existing one.
Projects
None yet
Development

No branches or pull requests

5 participants