-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Closed as not planned
Closed as not planned
Copy link
Labels
FrozenDueToAgeLanguageChangeSuggested changes to the Go languageSuggested changes to the Go languageProposalWaitingForInfoIssue is not actionable because of missing required information, which needs to be provided.Issue is not actionable because of missing required information, which needs to be provided.v2An incompatible library changeAn incompatible library change
Milestone
Description
Problem:
I recently refactored some code (moving a bunch of multi repos modules into a mono repo one), which gave me this solution:
// --- old module
type SomeInterface interface {
DoStuff()
}
// --- new module (in the monorepo)
type SomeInterface interface {
DoStuff()
}
// --- a third module (I actually have close to a hundred different modules like this)
type SomeProducerInterface interface {
Get() old.SomeInterface
}
// --- a consumer module
func Use(SomeProducerInterface) { /* ... */ }
// --- an implementation, that I refactored to use the new API (this is also in the monorepo)
type impl struct{}
func (impl) Get() new.SomeInterface {
return nil
}
consumer.Use(impl{}) // does not compile:
/*
./prog.go:28:13: cannot use impl{} (value of type impl) as type third.SomeProducerInterface in variable declaration:
impl does not implement third.SomeProducerInterface (wrong type for Get method)
have Get() new.SomeInterface
want Get() old.SomeInterface
*/
// other example:
type SomeProducerInterface interface { // new equivalent definition of SomeProducerInterface but in the monorepo
Get() old.SomeInterface
}
var _ third.SomeProducerInterface = SomeProducerInterface(nil) // does not compile
Current workarounds:
- Remove all usage of the old interface definition within the program.
Not Possible for me (prevents me from doing incremental refactors). - Create stub types and update the old module. (tedious but work, my current solution)
My main complain with this is that my new monorepo librairy does not consume all of the stubs, so sometime consumers see unexpected types errors when they upgrade my monorepo and they have to manuallygo get
all packages that have been stubbed to use the stubs too.
Solution:
As a solution I propose that for all sets of "compatible types", the compiler picks one of them and use it in all usage of the other compatible types in the set within the program.
Two types are said to be compatible:
- if they are interfaces, they both need to have the same methods and for all methods arguments and return types need to be compatible in order to be considered compatible, for cyclic case of this definitions it is considered compatible.
- for everything else, the types have to be the same (current rules).
TODO: Should the behaviour of which type of the compatible set is picked be defined in the spec ?
This would ensure consistency across compiler.
Non breaking changes:
Programs like shown above will now compile.
Breaking changes:
Interfaces types gotten through reflect
may now point to other (compatible) interfaces unexpectedly.
So things like this would give a different answer:
package main
import (
"fmt"
"reflect"
)
type A interface {
DoStuff()
}
type B interface {
DoStuff()
}
type impl struct{}
func (impl) Get() A {
return nil
}
func main() {
t := reflect.TypeOf(impl{}).Method(0).Type.Out(0)
// Right now t is certain to be A
// But after the change it may be interned to B
fmt.Println(t.Name())
}
Metadata
Metadata
Assignees
Labels
FrozenDueToAgeLanguageChangeSuggested changes to the Go languageSuggested changes to the Go languageProposalWaitingForInfoIssue is not actionable because of missing required information, which needs to be provided.Issue is not actionable because of missing required information, which needs to be provided.v2An incompatible library changeAn incompatible library change