// At type of thing.
type Object interface {
}
// An instance of something.
type Instance interface {
Type() Object // The instance's type.
}
// A type of animal.
type Species interface {
Object
}
// An individual animal.
type Animal interface {
Instance
Type() Species // The animal's species.
}
I'm going to have some functions where I want to be able to pass in
specific animals and manipulate them somehow. For example, I might
want to know if a specific animal can eat lettuce.
func CanEat(a Animal, f Food) bool {
return a.Type().CanEat(f)
}
On the other hand, sometimes I might want to have storage that is
instance type agnostic.
var namedObjects map[string]Instance
func NamedCanEat(s name, f Food) bool {
obj, ok := namedObjects[s]
if !ok { return false }
switch a := obj.(type) {
case Animal: return CanEat(a, f)
default: return false
}
}
The problem is I can't make a "parent" interface that returns a
different type from one of its functions than the child, even though
the child's return type is seemingly compatible. That is
Type() Object
and
Type() Species
cannot coexist inside Animal because they return two different
interfaces, even though everything that is a species is also an
object.
main.go:23: duplicate method Type
I thought the answer might be that I should create functions that
would implement Eats for their parent interface and all the sub-
interfaces that do something different than the parent. But that does
not work either because you can't define functions with interfaces as
their receiver (doh!).
main.go:14: invalid receiver type Object
Because of that error, I suspect my thinking here regarding interfaces
is Very Wrong. I'd be interested in any pointers you guys have on the
right way to do an object system like this. Right now I'm thinking
that maybe a similar approach, but with struct types rather than
interface types, was the way I actually should have been heading.
-- James
> I'm trying to create an object system for a personal project I'm
> working on, but I've run into a little snag. For brevity, I've reduce
> the problem to one of describing the world and some animals:
>
> // At type of thing.
> type Object interface {
> }
>
> // An instance of something.
> type Instance interface {
> Type() Object // The instance's type.
> }
>
> // A type of animal.
> type Species interface {
> Object
> }
>
> // An individual animal.
> type Animal interface {
> Instance
> Type() Species // The animal's species.
> }
It can be hard to answer this kind of question about a hypothetical
set of types, since it's not clear which aspects are fundamental and
which are not.
Go's interfaces mean that you don't have to think in terms of a
heirarchy. No inheritance is required for an object to meet an
interface. In this example you're building a double inheritance
heirarchy of objects and their types, but I'm not sure what that is
buying you.
In particular, why does the Species interface incorporate all the
methods of the Object interface? Methods are attached to real types,
not to interface types. A real type can satisfy both the Object
interface and the Species interface without your having to include
Object in Species. Similarly, why does the Animal interface
incorporate the Instance interface?
Instead of doing inheritance at the interface level, do it at the
struct level using embedded fields. That would look more like
type RabbitSpecies struct {
// Rabbit info
}
func (p *RabbitSpecies) CanEat(f Food) bool {
return f == Lettuce
}
type Rabbit struct {
RabbitSpecies
Name string
}
Now every Rabbit that you create supports the methods defined for
RabbitSpecies, including the CanEat method.
type Eater interface {
CanEat(Food) bool
}
func CanEat(a Eater, f Food) bool {
return a.CanEat(f)
}
Now you can call the CanEat function with any object which defines a
CanEat method.
Ian
To unsubscribe from this group, send email to golang-nuts+unsubscribegooglegroups.com or reply to this email with the words "REMOVE ME" as the subject.
Languages that let you do this kind of thing typically
have the same representation for both objects and
interfaces - a single pointer. So as long as the set of
values for one is a subset of the set of values for the
other, you can flip the type without affecting the code
that would execute.
Go uses different representations: a pointer is a pointer,
and an interface is a two-word structure. A function that
returns a *Bird returns one word (the pointer), and a function
that returns a Flyer returns two words (the interface).
Even different kinds of interfaces have different layouts:
an io.ReadWriter has the Write method at slot 2 in the itable,
while an io.Writer has the Write method at slot 1. So you can't
even use a func(io.Writer) where a func(io.ReadWriter) is
expected.
There's more detail about the representation of interfaces at
http://research.swtch.com/2009/12/go-data-structures-interfaces.html
Russ