-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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: Go 2: spec: add self
type for use within interfaces
#28254
Comments
There is a workaround for this problem within the existing type system. To have a clonable interface
you just make the method for cloning have a name specific to Then, to implement with a concrete struct
It is not as succinct as In my experience, real uses of |
It's noteworthy that many other type systems are much more complicated than Go's; when you have things like covariance then the
What you have provided isn't a terrible solution to the issue. It still suffers from designing your structs around the interface, though, rather than designing the interface around a pattern. For instance, I wouldn't be able to describe a recurring pattern in a foreign package. |
I'm not clear on whether this can be implemented. Presumably It's also worth noting that |
I'm not sure what you mean here. The package where the conversion occurs has access to the type descriptor for (I suspect we could even write such a function using the |
@bcmills That is true, it may be possible to construct the function using |
So basically you want to replace interface name in parameter type as "self" word. Does the current solution not suit your needs? Why such complication? This will affect changes in existing editors, libraries etc. Also "self" word is so "pythonish". Let's go more in C/C++ direction than Python. |
No, it's not the interface name Look at the Cloner interface that I have provided, it matches |
Even though this issue is quite old, as I also often need this feature, and since we have the generic approach now, here are my two cents. I played around with generics, but unfortunately they also don't seem to solve this particular problem. You can have generic Nodes and Edges, like described in the proposal, but they don't help you with the issue described here. If you want to return the interface itself, then you need to be aware of the interface. Duck Typing would be much more complete when having something like the
Another example of where this would be helpful:
This would enable us to finally have a type-safe structured-log that we can use when writing libraries. |
Can't type LogFieldBuilder[L Logger] interface {
WithString(key, value string) LogFieldBuilder[L]
WithInt(key string, value int) LogFieldBuilder[L]
Build() L
} Yeah, it'll return the interface, but the returned interface will still provide the same methods and the |
Unfortunately, when returning the interface, the implementation needs to be aware of the interface:
So now the logger would need to be aware of Additionally, when only having a self-argument in the generic argument list, it could be considered that we can always infer that, to prevent a bit of the "Generic Poisoning" ( mentioned by @deanveloper ) |
A late update: The requested self-referential function signature inside the interface works well with type parameters in the current dev branch: type Cloner[C any] interface {
Clone() C
}
type Cloneable int
func (c Cloneable) Clone() Cloneable {
return c
}
func CloneAll[T Cloner[T]](originals []T) (clones []T) {
for _, c := range originals {
clones = append(clones, c.Clone())
}
return clones
}
func main() {
origs := []Cloneable{1, 2, 3, 4}
fmt.Println(CloneAll(origs))
} Note how the CloneAll function is parametrized with |
You are right @christophberger . It's not very easy to read but it is awesome that it works! |
@mier85 - I confess I have unconsciously increased the reading difficulty by choosing slice parameters (which means additional square brackets). I am working on a better example for a blog article, even though you beat me already at that! :) Edit: for reference, here is my article: How to use generics for creating self-referring interfaces |
A personal experience that may shed some photons on the matter: So I ran into this problem head-first during this weekend. I couldn't solve it without introducing some serious generic heavy-duty-machinery on the user's side since I had built a hierarchical generic interface (if that's what you call it?). In the end I ended up ditching the generic approach on the interface methods and generifying the algorithm's implementation. You can find a monologue at golangbridge. |
I ran into this problem, when I tried to call an "overwritten" method, of a type with an embedded type.
|
It's not what you did, not really. You called the The closest you can get to the fluent interface you seemed to be going for would be augmenting func (b *B) SetSomething(s interface{}) MyInterface {
b.A.SetSomething(s)
return b
} You have to keep in mind that Go does not have inheritance. The problems you are used to solving using inheritance in other languages you can't solve this way in Go. |
Thank you, now I understand that even if |
@TAR5, it's not even forwarding: when you call a method that is not defined on an embedding type, your call just falls through to the method on the embedded value, which is completely oblivious of anything but itself. But, yeah, you got the gist of it. This proposal is about being able to satisfy an interface by returning the concrete (not interface) value. So that, for your example, you could have
satisfying type MyInterface interface {
SetSomething(v interface{}) <magic>
} where |
https://medium.com/@mier85/self-referencing-interfaces-in-golang-1-18-bcd6b5701992 Is this what you're looking for? edit: didn't realize it's already poested here. |
I believe I am having a comparable problem here where I need the interface to specify methods that can return values of the same type as they are called on. I don't see how it can be done given the current spec. |
@Zyl9393 That is worth discussion, but not on this issue. As the Stack Overflow answer shows, there are ways to do what you want, even if they are less convenient than you desire. I don't think there is anything to do on this issue now that we have generics and generic interface types. So I'm going to close this. Please comment if you disagree. |
Mail group discussion regarding gonum's type Matrix[T self] interface {
At(i, j int) float64
Dims() (r, c int)
T() T
} |
Sorry for bother everyone, I find another use case, when I try to import type FromIter[T any] interface {
FromIter(iter Iter[T]) ???
} The return type are those who implement FromIter, not a specific type. |
Hi @leaxoy , A closed GitHub issue is perhaps not the best location to discuss a programming problem. I would suggest that you move your question over to a discussion group like the Go Forum or the Golang-Nuts mailing list. You will get many more eyes on your issue in either of these places. To move forward quickly, start there by providing the following information: How is And maybe you can even provide an example of the exact problem in the Go Playground. I already tried to figure out what the problem is, but I seem not to have recreated your exact scenario. See you in the Go Forum or in the Golang-Nuts mailing list! |
The Problem
Let's make an interface called
Cloner
, which describes things that can clone themselves.As any good Go interface should be designed, it should describe behaviors rather than making implementations conform to the interface.
So, let's see our implementations:
As you can see, the two
Clone()
methods have separate signatures. This is unfortunate as this means it is impossible to have an interface describe both types, even if they have similar patterns.Current Solution
We could "solve" this issue like so:
Then, we'd design the
Foo
andBar
functions to returnCloner
s rather than themselves.This causes a few issues, though.
Cloner
s are returned, this means callingClone()
directly on aFoo
orBar
would return aCloner
, which means we need to cast back to Foo, which is a big readability issue.Foo
andBar
directly, as it causes unnecessary boxing/unboxing, although I'm not sure if the compiler optimizes this away.This is also apparently not the most intuitive solution, as it needs to be answered in the FAQ: https://golang.org/doc/faq#t_and_equal_interface
This is "solved" by generics, although not really. This is detailed at the bottom of the proposal.
The proposed solution:
self
Instead, our Cloner interface would look as follows:
The implementations would not need to conform to the interface, so
Foo.Clone
would remainfunc (f *Foo) Clone() *Foo
, andBar.Clone
would also remain the same.Then, it could be used as follows:
Bada-bing Bada-boom, we've solved our issue with relative simplicity.
Footnotes
Footnote: Syntax and compatability
"self" would also be a predeclared type, so if anyone has a type named "self" in their package, then that will be used rather than the predeclared
self
.This goes without saying, but we aren't locked to the name "self" or anything. Also, adding a special character to show that the "self" type is special, such as
@self
or#self
, may be a good idea. I am personally not a fan of this, as it don't feel very Go-like.Footnote: Generics
I referenced above that the issue is partially solved by generics. This section illustrates how generics can solve some issues, but is not a full solution.
Here are the two solutions which can be used to solve our problem:
Solution 1 prevents you from checking if
t
is Cloneable at runtime.Solution 2 works, although it suffers from "Generic Poisoning" as then every function that wants to take a
Cloner
as an argument would then need parametric types.Solution 2 ALSO suffers from the fact that
T
is not guaranteed to be the same as the receiver, leading to the following also implementingCloner
:This is not a true
Cloner
implementation! It returns a separate type from its receiver.The text was updated successfully, but these errors were encountered: