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: permit function assignment when parameters are assignable #27371
Comments
I don't think this is possible at the moment - a function can't have many types at once in Go. Have you looked at the recent Go2 generics draft yet? It would likely cover your use case here. |
@mvdan If I'm reading this correctly, I think it is technically possible. The function doesn't have many types, we're instead applying assignability conversions on the results. The interesting aspect is that the outer function is variadic. I think it could be done, it's just potentially confusing. |
@ianlancetaylor I think you might have mis-understood the proposal. While accepting function returns in a variadic function call would also be interesting, this proposal is as follows:
I think using ellipses in my original proposal was a confusing choice, so I updated the wording a bit above. It's about allowing assignment of a function with concrete returns to a type where the return values are specified by interfaces implemented by the return types of the function being assigned... if that makes sense. |
Ah, sorry for misunderstanding. In that case I agree with @mvdan: I'm not sure this is possible in general. |
@mvdan I'm not sure I understand it correctly, but I don't see why the proposal needs a function to have multiple types. It only says a func value of one type (say |
Agreed, that a func taking N arguments ought to be assignable to a type |
@networkimprov I think you're experiencing the same misunderstanding that ian did, and I think that's my fault for being unclear. Here's a (hopefully) better example: var X func(int) (InterfaceType, error)
func Test(x int) (ConcreteType, error) { /* ... */ }
// currently invalid, but the proposal is to make this a valid assignment
X = Test i.e. |
Weak typing it is, i.e. generally bad idea. And IDE can reduce the burden to acceptable level anyway. See the video: https://www.youtube.com/watch?v=EA6wzEVW7P4&feature=youtu.be |
@sirkon I appreciate the thought, but I am familiar with IDEs for Go... I am less concerned with the writability of code, and more with the readability as well as the guarantees provided by being able to write my functions in terms of concrete types. @mvdan is right, the new generics proposal for Go2 does cover this need. Although I am confused why this would necessarily be a Go2 feature. As far as I can tell (which is admittedly limited), it would not prevent existing code from working as expected and it does not claim any new keywords. It might certainly be more complicated than I am imagining to implement after having looked through the compiler typecheck source code, but it could perhaps be a special case when a function is the value being assigned/assigned to. |
We're treating all language changes as Go 2 issues, backward compatible or not. |
This seems related to https://golang.org/doc/faq#covariant_types . |
I understood you, I suggest adding your better example to the proposal blurb. But a set of arguments (CASE 1&2) is not a type, whereas T is. And T does look rather generic :-) |
@networkimprov no, that's not right. This has nothing to do with input parameters. I'm speaking specifically for output parameters. If a function returns types (A, B), then the proposal is to make that assignable to a value expecting a function with return types (X, Y), provided A is assignable to X and B is assignable to Y, and of course provided the input parameters are the exact same. @ianlancetaylor Makes sense regarding all proposed changes being go2, my misunderstanding. |
Assignments in Go generally do not allocate unless they are assignments to interface types. For that use-case, an explicit conversion seems more in line with the rest of the design of the language, along the lines of type F = func(int) (A, B)
var X F
[…]
X = F(Test) // Explicit conversion, but avoids boilerplate.
[…] |
@bcmills I'm alright with that - explicit conversion seems like a reasonable implementation to me. |
It's worth noting that if we are going to support implicit assignment for functions according to these guidelines, then we should support them for methods too. For example type T int
func (T) M(int) {}
type I interface {
M(interface{})
}
// One would expect this to work if this proposal were adopted.
var x I = T(0) And of course similarly we should be able to assign one interface type to another if the method parameters are assignment-compatible as described in this proposal. (If we require an explicit conversion as @bcmills suggests, we could require the explicit conversion between interface types.) This type system complexity is all designed to save people from writing one line wrapper functions. The additional type and language complexity does not seem worth the benefit. It may be interesting to consider that the wrapper function can be generated using generics as suggested in the current design draft. It requires a separate wrapper function for each number of input parameters and number of result parameters, and the type arguments can not be inferred. This would look something like the following. It's a fair amount of boilerplate for a simple example like this, but presumably one would only be reaching for this ability when there were several similar functions. // alias for readability
type Any = interface{}
func Fails(try func() (Any, Any)) {
fmt.Println(try())
}
func Try() (float32, int) {
return 1.4, 2
}
contract wrappable(t1 T1, t2 T2, t3 T3, t4 T4) {
_ = T3(t1)
_ = T4(t2)
}
func Wrapper(type T1, T2, T3, T4 wrappable)(f func() (T1, T2)) func() (T3, T4) {
return func() (T3, T4) {
r1, r2 := f()
return T3(r1), T4(r2)
}
}
func main() {
Fails(Wrapper(float32, int, interface{}, interface{})(Try))
} All in all the benefits of this proposal do not seem worth the additional language complexity. |
In Go, it is valid to use the outputs of a function as inputs to another when there are the same number of outputs as inputs, and when each output's type is assignable to the type of the corresponding input (CASE 1):
Similarly, we can use the results of a function call to return from a function as long as both functions have the same number of outputs and the output of the called function is assignable to the corresponding output of the returning function (CASE 2):
However, it is not possible to do the following. It fails with
cannot use Try (type func() (float32, int)) as type func() (interface {}, interface {}) in argument to Fails
(CASE 3):I propose that the this third case be enabled in Go. Specifically, I propose we allow a function whose signature is
func(/* ... */) (A0, A1, ...An)
to be assignable tofunc(/* ... */) (B0, B1, ...Bn)
whereAn
is assignable toBn
for alln
(and of course when the inputs are also compatible via the existing rules).Because CASE 2 is valid, it seems like it would only be a matter of auto-generating a one-line wrapper function during the assignment. As an example, let's look at a working version of CASE 3:
This would make it easier to maintain type safety in user code while still interfacing with libraries in terms of interfaces.
Edit:
A better example:
The text was updated successfully, but these errors were encountered: