-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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: allow method declarations on function-local types #71562
Comments
Please fill out https://github.com/golang/proposal/blob/master/go2-language-changes.md when proposing language changes We'd also need to see much stronger evidence that this is useful across the ecosystem for it to be considered |
Related Issues (Emoji vote if this was helpful or unhelpful; more detailed feedback welcome in this discussion.) |
Outside of a function all identifiers are compiled in a scope such that they can all see all other package-scope identifiers. That permits methods to refer to each other, as in func (T) M1() { M2() }
func (T) M2() { M1() } Currently, within a function, identifiers are only visible after they have been declared. That means, for example, that you can't write func F() {
fn := func() { fn() }
} You have to instead write func F() {
var fn func()
fn = func() { fn() }
} If we permit methods within a function, we need to exact set of scoping rules that apply to them. |
Good point, I do frequently have to write code such as
To allow a locally defined function to call itself recursively, so if that was changed to be allowed it would be welcome. |
Changing that would be a different proposal. I recall that it's been proposed before, but I didn't have any luck finding the earlier discussion. |
that's #33167 |
I want this in the abstract, but I think methods shouldn't be able to close over variables, so it won't work.
Too big of a can of worms. |
I recall some earlier proposals, similar to but not exactly #47487, about various different mechanisms to make a value that implements an interface by building it out of one closure per method. Unfortunately I can't find it now, but I recall that one of them proposed something like this: type Example interface {
A()
B() string
}
func WantsExample(e Example) {
// ...
}
func main() {
WantsExample(Example{
A: func () { /* ... */ },
B: func () string { /* ... */ },
})
} In that case the interface is effectively being implemented based on data captured into the closures, thus partially answering @earthboundkid's question. I don't recall all of what caused that proposal to run into trouble, but one challenge I can already see with it just having written it out is that this language feature would presumably need to construct some sort of special "union-closure" that combines all of the closures of all of the functions declared inline. That's weird enough even in this simple example, but potentially even weirder if one of the function pointers had been passed in as an argument having already captured variables from some other scope into its own closure. I'm mentioning this only as a substitute for linking to that previous proposal as context that might help with discussion on this proposal; I'm not intending to make a counterproposal. I was not able to figure out what to search for to find it. I'm sorry if I'm misremembering details from it. Edit: Naturally, I found it immediately after I posted 🙄 #25860 |
I've often wondered why Go's func declaration was special in that you can't use it within a function (for methods or even ordinary functions), even though that would be convenient. One possible reason is that it would create a parsing ambiguity: when parseStmt sees I'm probably in the minority, but the nature of my work demands a lot of recursive functions, and I prefer to use local functions to reduce scope and avoid polluting the namespace of the package. (The alternative is to extract a package-level struct and define methods on it, but for small functions that creates a detour for the reader.) In principle I too would welcome a way to write recursive local named functions using a As for local methods on local types: the obvious implementation is to collect all the free variables of the methods and add them as hidden fields of the local type, so the example below would have a hidden, unnameable pair of fields func makePair(x, y int) interface { first() int; second() int } {
type Pair struct{}
func (Pair) first() int { return x }
func (Pair) second() int { return y }
return Pair{}
} In summary:
[Edit: my first draft was in error about MyObject; changed to use makePair example.] |
Based on the discussion above about the complexities of this feature concerning scoping and closures, this is a likely decline. Leaving open for four weeks for final comments. |
Proposal Details
Methods cannot be declared inside a function scope. For types that are declared inside a function, it is occasionally useful to also be able to define methods on those types in the same function rather than having to move the entire type and its methods outside of the function. The benefits are
x.f()
, instead of being regarded as second-tier that would requiref(x)
An example demonstrating the first point
Example demonstrating second point
The second example can be rewritten without a method receiver, where a regular function accepts a
*MyObject
type as the first argument, but this creates a distinction between types declared globally and those declared locally.A more advanced version of this proposal would be to allow the local methods to close over bindings declared before the method, thus allowing an inner method receiver to invoke methods on an outer method receiver.
I could imagine that local methods that do not support closing over local bindings could be implemented by lifting the type and its declaration to the global scope, but keeping the scope of the bindings local to the function they were defined in.
The text was updated successfully, but these errors were encountered: