Reputation: 5012
Why two named interfaces with same methods are treated as different ones - and how can you avoid that?
So let's say we have a guy who likes to eat products (Eater). He doesn't care what products he eats, he only wants to be pointed out from where he can fetch new products. In other words he wants product service, but doesn't care what products product service will produce. In concrete implementation we will try to feed him with apples, so we will provide him with appleService.
Here is the result:
./main.go:9: cannot use appleService (type *service.AppleService) as type eater.ProductServiceI in function argument:
*service.AppleService does not implement eater.ProductServiceI (wrong type for New method)
have New() service.ProductI
want New() eater.ProductI
Interfaces service.AppleI
and eater.AppleI
have same method Eat()
and nothing else but golang considers them as different ones. Why and how to avoid that? According to duck typing this should work because what actually ProductServiceI
requires is that provided struct has Eat()
method - it shouldn't care about what name has interface (service.ProductI
vs eater.ProductI
).
Below is full code:
==> ./main.go <==
package main
import "./apple/service"
import "./eater"
func main() {
appleService := &service.AppleService{}
// func eater.New(productService ProductServiceI)
appleEater := eater.New(appleService)
appleEater.EatUntilHappy()
}
==> ./eater/eater.go <==
package eater
type ProductServiceI interface {
New() ProductI
}
type ProductI interface {
Eat()
}
type Eater struct {
productService ProductServiceI
}
func New(productService ProductServiceI) *Eater {
return &Eater{
productService: productService,
}
}
func (a *Eater) EatUntilHappy() {
for i:=0; i < 5; i++ {
product := a.productService.New()
product.Eat()
}
}
==> ./apple/service/service.go <==
package service
import "./apple"
type ProductI interface {
Eat()
}
type AppleService struct {
}
func (a *AppleService) New() ProductI {
return &apple.Apple{}
}
==> ./apple/service/apple/apple.go <==
package apple
import "fmt"
type Apple struct {
}
func (a *Apple) Eat() {
fmt.Println("mniam, mniam")
}
I thought it doesn't matter what name or what import path has interface as long as declaration is the same.
Upvotes: 4
Views: 8076
Reputation: 5012
I will answer my own question
Firstly I just found out answer about why those interfaces are not considered similar in go in their official faq: http://golang.org/doc/faq#t_and_equal_interface
Why doesn't type T satisfy the Equal interface?
Consider this simple interface to represent an object that can compare itself with another value:
type Equaler interface { Equal(Equaler) bool }
and this type, T:
type T int func (t T) Equal(u T) bool { return t == u } // does not satisfy Equaler
Unlike the analogous situation in some polymorphic type systems, T does not implement Equaler. The argument type of T.Equal is T, not literally the required type Equaler.
In Go, the type system does not promote the argument of Equal; that is the programmer's responsibility, as illustrated by the type T2, which does implement Equaler:
type T2 int func (t T2) Equal(u Equaler) bool { return t == u.(T2) } // satisfies Equaler
Even this isn't like other type systems, though, because in Go any type that satisfies Equaler could be passed as the argument to T2.Equal, and at run time we must check that the argument is of type T2. Some languages arrange to make that guarantee at compile time.
A related example goes the other way:
type Opener interface { Open() Reader }
func (t T3) Open() *os.File
In Go, T3 does not satisfy Opener, although it might in another language.
While it is true that Go's type system does less for the programmer in such cases, the lack of subtyping makes the rules about interface satisfaction very easy to state: are the function's names and signatures exactly those of the interface? Go's rule is also easy to implement efficiently. We feel these benefits offset the lack of automatic type promotion. Should Go one day adopt some form of polymorphic typing, we expect there would be a way to express the idea of these examples and also have them be statically checked.
Secondly in my case I can use anonymous interface interface { Eat() }
instead of declaring type ProductI interface { Eat() }
But in case of examples from go faq that is not possible. Consider this:
type Equaler interface {
Equal(Equaler) bool
}
type T int
func (t T) Equal(u Equaler) bool { return t == u.(T) }
If you would use the same trick here you will get
type T int
func (t T) Equal(u interface{ Equal(interface{ Equal(...) bool }) bool }) bool { return t == u.(T) }
In other words you end up with recursion - ups
Below is my updated code with this trick - it works - someone might find this trick useful.
==> ./eater/eater.go <==
package eater
type ProductServiceI interface {
New() interface { Eat() }
}
type Eater struct {
productService ProductServiceI
}
func New(productService ProductServiceI) *Eater {
return &Eater{
productService: productService,
}
}
func (a *Eater) EatUntilHappy() {
for i:=0; i < 5; i++ {
product := a.productService.New()
product.Eat()
}
}
==> ./main.go <==
package main
import "./apple/service"
import "./eater"
func main() {
appleService := &service.AppleService{}
appleEater := eater.New(appleService)
appleEater.EatUntilHappy()
}
==> ./apple/service/service.go <==
package service
import "./apple"
type AppleService struct {
}
func (a *AppleService) New() interface{ Eat() } {
return &apple.Apple{}
}
==> ./apple/service/apple/apple.go <==
package apple
import "fmt"
type Apple struct {
}
func (a *Apple) Eat() {
fmt.Println("mniam, mniam")
}
And the result :)
# go run main.go
mniam, mniam
mniam, mniam
mniam, mniam
mniam, mniam
mniam, mniam
Upvotes: 3
Reputation: 1269
Just for the sake of completion: a very similar case (methods with same signature exists, but Go does not find them) can occur if you use private methods in an interface (lower case method name).
That way Go restricts its searches for the method to the package that defines the Interface and therefore does not find the method inside the package that implements the method.
Actually, as I cannot think of any sane reason to use private methods in a public interface: use the same visibility for the methods of an interface as for the interface itself.
Upvotes: 1
Reputation: 4363
I think you have a couple of problems here. For one, you have defined the AppleI
interface twice, once in main and once in service. Second, you seem to be trying to do Python or Java (I'm guessing) style packages. It's worth reading up on packages
Here is a version of your code that compiles and runs.
==> kamil/main.go
package main
import (
"kamil/service"
)
type Program struct {
appleService service.AppleServiceI
}
func main() {
program := &Program{
appleService: &service.AppleService{},
}
apple := program.appleService.New()
apple.Eat()
}
==> kamil/service/service.go
package service
import (
"kamil/service/apple"
)
type AppleServiceI interface {
New() AppleI
}
type AppleI interface {
Eat()
}
type AppleService struct {
}
func (a *AppleService) New() AppleI {
return &apple.Apple{}
}
==> kamil/service/apple/apple.go (unchanged)
package apple
import "fmt"
type Apple struct {
}
func (a *Apple) Eat() {
fmt.Println("mniam, mniam")
}
That said, I don't think the approach you are taking is idiomatic GO and you will likely end up banging your head against it at some point :)
Upvotes: 4