Is there a way to implement a "is a" relationship between objects in Go ?

761 views
Skip to first unread message

Serge Hulne

unread,
Feb 5, 2011, 12:49:10 PM2/5/11
to golang-nuts
In object-oriented languages like C++, Python and Java (to name but
the most famous three), the respective languages provide at least two
handy relationship between objects namely : The "has a" relationship
and the "is a" relationship.

- If I understand correctly, the "has a" relationship can be
implemented in Go, in the form of a structure having as a member
another object of a given type (for instance a first structure
containing a second structure).

- I was wondering if there is also a possible way to implement (or
mimic) an "is a" relationship between objects (user defined types) in
Go ? in order to achieve the "specialization" effect which is obtained
in C++ or Python via the mechanism of inheritance ?

I might be mistaken, but I think that Go does (intentionally (*1) )
not support any such features (I am correct in assuming this ?).


Serge.


(*1) for the sake of simplicity, clarity, compile-time performance and
runtime performance.

bflm

unread,
Feb 5, 2011, 1:11:31 PM2/5/11
to golan...@googlegroups.com
On Saturday, February 5, 2011 6:49:10 PM UTC+1, Serge Hulne wrote:
- I was wondering if there is also a possible way to implement (or
mimic) an "is a" relationship between objects (user defined types) in
Go ? in order to achieve the "specialization" effect which is obtained
in C++ or Python via the mechanism of inheritance ?
Do you mean?:

Serge Hulne

unread,
Feb 5, 2011, 1:25:27 PM2/5/11
to golang-nuts
No, I mean:

- Object a has methods f(), g(), h() : a.f(), a.g(), a.h()

- Object b "is a" (paricular case of) a, therefore b "inherits" all
the methods and data members of a: b.f(), b.g(), b.h(), (unless one
redefines one of these methods for b (specialization obtained by
combining overloading and inheritance)).

Serge.

Andrew Gerrand

unread,
Feb 5, 2011, 1:31:37 PM2/5/11
to Serge Hulne, golang-nuts
What you want is struct embedding:
http://golang.org/doc/go_spec.html#Struct_types

Andrew

jimmy frasche

unread,
Feb 5, 2011, 1:35:31 PM2/5/11
to Serge Hulne, golang-nuts
Anonymous (also called embedded) fields. Read about it here:
http://golang.org/doc/go_spec.html#Struct_types

Also note that you can embed any named type including an interface
(very useful!). You can also embed interfaces in interfaces.

pgavlin

unread,
Feb 5, 2011, 2:05:21 PM2/5/11
to golang-nuts
On Feb 5, 12:35 pm, jimmy frasche <soapboxcic...@gmail.com> wrote:
> Anonymous (also called embedded) fields. Read about it here:http://golang.org/doc/go_spec.html#Struct_types
See also http://golang.org/doc/go_spec.html#Selectors

Serge Hulne

unread,
Feb 5, 2011, 2:40:49 PM2/5/11
to golang-nuts
Thank you Andrew, Jimmy and "pgavlin"

So basically, Go provides an "is a" relationship between
structures ... in the form of an "has a" relationship, (under the only
condition that the embedded object (the equivalent of the parent
class) be an "anonymous field").


This feature allows for specialization in Go (in the sense of :
inheritance + overloading).


A priori, this sounds like a very powerful feature.

In C++, before the introduction of templates, generics were obtainable
(at least within a tree of classes deriving from a common ancestor) by
defining a set of methods on a common ancestor (virtual) class and by
redefining said methods for the children classes, when needed
(overloading).

Would this modus operandi not provide generics in Go (within a set of
structures -embedding a "common ancestor"- ) ?

Serge.

jimmy frasche

unread,
Feb 5, 2011, 2:48:52 PM2/5/11
to Serge Hulne, golang-nuts
On Sat, Feb 5, 2011 at 11:40 AM, Serge Hulne <serge...@gmail.com> wrote:
> Thank you Andrew, Jimmy and "pgavlin"
>
> So basically, Go provides an "is a" relationship between
> structures ... in the form of an "has a" relationship, (under the only
> condition that the embedded object (the equivalent of the parent
> class) be an "anonymous field").

The relationship is between /named types/ which is important because
it's more powerful:
type X int
//methods on X
type Y struct {
X
} //has methods of Y

type Z interface { Z() }
type W struct {
Z
} //you can assign anything with a Z method to the anonymous field in W


> This feature allows for specialization in Go (in the sense of :
> inheritance + overloading).
>
>
> A priori, this sounds like a very powerful feature.
>
> In C++, before the introduction of templates, generics were obtainable
> (at least within a tree of classes deriving from a common ancestor) by
> defining a set of methods on a common ancestor (virtual) class and by
> redefining said methods for the children classes, when needed
> (overloading).
>
> Would this modus operandi not provide generics in Go (within a set of
> structures -embedding a "common ancestor"- ) ?
>
> Serge.
>
>

I think I see what you're saying but that's not really generics so much

Serge Hulne

unread,
Feb 5, 2011, 3:21:25 PM2/5/11
to golang-nuts
Hi Andrew,

Since Go provides inheritance/specialization (as you have correctly
pointed at) then why does container/stringvector.go actually *copy*
(some of) the methods of container/vector.go instead of simply
inheriting them by embedding (and redefining them when appropriate) ?


Serge

Rob 'Commander' Pike

unread,
Feb 5, 2011, 3:25:33 PM2/5/11
to Serge Hulne, golang-nuts
An exercise for the reader. Try it.

-rob

chris dollin

unread,
Feb 5, 2011, 3:29:15 PM2/5/11
to Andrew Gerrand, Serge Hulne, golang-nuts
On 5 February 2011 18:31, Andrew Gerrand <a...@golang.org> wrote:
> What you want is struct embedding:
>  http://golang.org/doc/go_spec.html#Struct_types

Struct embedding doesn't do what OO inheritance does
(viz, method overriding). So they may not (just) want
struct embedding -- they may want (or find useful) first-class
functions or interfaces.

Chris

--
Chris "allusive" Dollin

Steven

unread,
Feb 5, 2011, 3:49:02 PM2/5/11
to Serge Hulne, golang-nuts
On Sat, Feb 5, 2011 at 3:21 PM, Serge Hulne <serge...@gmail.com> wrote:
Hi Andrew,

Since Go provides inheritance/specialization (as you have correctly
pointed at) then why does container/stringvector.go actually *copy*
(some of) the methods of container/vector.go instead of simply
inheriting them by embedding (and redefining them when appropriate) ?


Serge

Look at the types. vector.Vector is a []interface{}. vector.StringVector is a []string. The methods of vector.Vector only know how to operate on a []interface{}. They can't be "inherited" to work on a different type ([]string). So the methods for vector.StringVector are generated by the build system so that they know how to work with a []string. This makes vector.StringVector more efficient and type safe. In C++ or Java, this would be done with generics, not inheritance. Go doesn't (yet) have generics, so this is handled in the build system, which makes a copy of the source file and replaces the types.

Note that embedding doesn't allow overriding, only shadowing:

To get virtual methods, you need either closures or interfaces. These can be initialized on a value by value basis.

Serge Hulne

unread,
Feb 5, 2011, 3:54:35 PM2/5/11
to golang-nuts
On Feb 5, 9:25 pm, "Rob 'Commander' Pike" <r...@golang.org> wrote:
> An exercise for the reader.  Try it.
>
> -rob


Ok, I will try it tomorrow !

It seems to work on the following snippet hereunder, but perhaps this
example is too simplistic:

Serge.



///////////////////
package main

type T0 struct {
x int
}

func (recv *T0) M0() {
println("Hello from T0 !")
}

type T1 struct {
y int
}

func (recv T1) M1() {
println("Hello from T1 !")
}

type T2 struct {
z int
T1
*T0
}

func (recv *T2) M1() {
println("Hello from T2 !")
}

func main() {

var p1 T1
var p2 *T2

p1.M1()
p2.M1()

}
///////////////////////

Charles Thompson

unread,
Feb 5, 2011, 4:07:44 PM2/5/11
to golang-nuts
Here's an example that ties the concept of "is a" and go interfaces
together.

Let's say you wrote a function that bubble sorts a slice of integers.

func BubbleSort(array []int) {
for i := 0; i < len(array); i++ {
for j := 0; j < len(array)-i-1; j++ {
if array[j] > array[j+1] {
array[j], array[j+1] = array[j+1], array[j]
}
}
}
}

This function is not the best because it only sorts an int slice. Want
to sort something else? Forget about it.

Instead, you can make it generic by declaring an interface called
Sortable that contains methods len, less, and swap. These methods
provide the core mechanics required by bubble sort.

type Sortable interface {
len() int
less(i, j int) bool
swap(i, j int)
}

func GenericBubbleSort(data Sortable) {
for i := 0; i < data.len(); i++ {
for j := 0; j < data.len()-i-1; j++ {
if data.less(j+1, j) {
data.swap(j+1, j)
}
}
}
}

Now let's get back to the original question. How do you make "is a"
relationships happen? Simple. Let's declare a new type called
IntStruct that "is a" slice of ints, and associate methods len, less,
and swap to it.

type IntSlice []int //is a slice of ints

unc (i IntSlice) len() int {
return len(i)
}

func (i IntSlice) less(a, b int) bool {
if i[a] < i[b] {
return true
}
return false
}

func (i IntSlice) swap(a, b int) {
i[a], i[b] = i[b], i[a]
}

The type InSlice is a slice of ints, and because it also implements
Sortable by virtue of its len, less, and swap methods so you can pass
it into your new genericized bubble sort function.

Charles

Serge Hulne

unread,
Feb 5, 2011, 5:28:55 PM2/5/11
to golang-nuts
Hi Charles,

Thanks you for your example !

From your example, it seems that the closest thing to an "is a"
relationship between objects in Go is the rule (loosely quoted)
whereby "a method which is defined for a given interface is also valid
for all types that implement said given interface".

I guess that is as close as Go will ever get to inheritance and
generics !

I cannot decide if this is a good thing or a bad thing for the
following reason:


. The pro: It "flattens" inheritance trees: They have only tow levels
(the parent object which is the interface and the children (the set of
all objects which implement said interface).

. The contra: The absence of inheritance (e.g. as known from C++ or
Python), which leads to a lot of code duplication (as we have seen
from the example of container.vector.go and container/
stringvector.go).



Perhaps something eludes me, but it seems to me that the absence of
inheritance in Go (in the usual sense of the term, when dealing with
"classes" as in C++, Python and Java), leads to a lot of code
duplication, which in turn will translate into a lot of maintenance.

Say, for instance, you modify the container/vector.go module at some
point, then you will have to propagate said changes *by hand* to
container/stringvector.go, container/intvector.go etc ...basically
rewriting the same code over and over again.


Perhaps I have misunderstood the idea, but I find the example of
container/vector vs container/stringvector really disturbing :

. as far as maintenance is concerned
. as far as structured programming is concerned

(Because of the mandatory duplication of code, itself linked to the
fact that the closest thing to inheritance or generics there is in Go
consists of interfaces and implementations thereof).

When studying C++, Java and Python it struck me that one very strong
point that all thee languages have in common is their build-in ability
to map the (inheritance-related) tree structures of classes which
represent real-world objects which by nature possess hierarchical
relationships, such as widgets from a GUI tool kit, employees in an
organization, and more generally : modelling real-world objects.

Unless I have misunderstood, this feature, as such, seems to be absent
from Go (as the container/vector example seems to indicate).

Is this correct ?

Serge.

jimmy frasche

unread,
Feb 5, 2011, 6:22:44 PM2/5/11
to Serge Hulne, golang-nuts
How could inheritance cut down any of the code in container/vector?
(which has been said is generated automatically, not by hand, re
maintenance nightmares)

ron minnich

unread,
Feb 5, 2011, 6:44:32 PM2/5/11
to Serge Hulne, golang-nuts
On Sat, Feb 5, 2011 at 2:28 PM, Serge Hulne <serge...@gmail.com> wrote:

> When studying C++, Java and Python it struck me that one very strong
> point that all thee languages have in common is their build-in ability
> to map the (inheritance-related) tree structures of classes which
> represent real-world objects which by nature possess hierarchical
> relationships, such as widgets from a GUI tool kit, employees in an
> organization, and more generally : modelling real-world objects.


I keep wishing I could find the Smalltalk retrospective article in
which it was pointed out that it is a bit odd to have processes
derived from lists, which was done because they wished to have
processes arranged in a linked list. The point being made in the paper
that it was hardly an intuitive relationship. Yes, we can keep finding
cases where inheritance makes sense; that doesn't establish that it
will always or even in most cases make sense. It just means it does at
times.

ron

Jonathan Wright

unread,
Feb 5, 2011, 6:49:18 PM2/5/11
to Serge Hulne, golang-nuts
Hi Serge,

On Sun, Feb 6, 2011 at 11:28 AM, Serge Hulne <serge...@gmail.com> wrote:
. The contra: The absence of inheritance (e.g. as known from C++ or
Python), which leads to a lot of code duplication (as we have seen
from the example of container.vector.go and container/
stringvector.go).

Perhaps something eludes me, but it seems to me that the absence of
inheritance in Go (in the usual sense of the term, when dealing with
"classes" as in C++, Python and Java), leads to a lot of code
duplication, which in turn will translate into a lot of maintenance.

Code sharing was one of the things class hierarchies promised to deliver. Over the last decade or two, deep class hierarchies have largely failed to deliver on this promise and are now believed to create more problems than they solve.

My impression is that today, the best practice is to favor-composition-over-inheritance and don't-burn-your-base-class (don't require a specific base class to get functionality). Is-a relationships are now solely implemented through interfaces. Inheritance is used as an internal implementation detail with base classes to share functionality between classes often hidden privately within a package. In this model, class-inheritance ends up as purely an internal implementation feature, rather than a way of doing is-a relationships.

This model of using public interfaces and private implementation sharing works well in Go. Interface types are made public and internal implementation sharing is handled through other techniques.

Is-a style code sharing leads to the sub-classes being tightly coupled with the base-classes and both fragile to change. This sort of tight coupling is best kept within a package and often becomes unwieldy when spread across many packages, or worse, between libraries and applications.

In conclusion, Go not having is-a based method inheritance is not a significant failing and doesn't result in code duplication. The problems that is-a based inheritance caused lead to it now being recommended to use it in a style that prevents the promised de-duplication benefits.

Thanks,
Jonathan Wright.

salvatore

unread,
Feb 5, 2011, 10:07:35 PM2/5/11
to golang-nuts
There's an anti-OOP article from a die hard OOP-hater:
http://www.geocities.com/tablizer/oopbad.htm

That author has arguments against inheritance similar to what you
stated. It's entertaining to read.


On Feb 6, 7:49 am, Jonathan Wright <qua...@gmail.com> wrote:
> Hi Serge,
>

Serge Hulne

unread,
Feb 6, 2011, 3:08:52 AM2/6/11
to golang-nuts
Hi Jonathan,

> My impression is that today, the best practice is to
> favor-composition-over-inheritance and don't-burn-your-base-class (don't
> require a specific base class to get functionality).

This sounds interesting, but I am not familiar with the concept of
"composition" in the context of Go.

Could you please elaborate on this concept of "composition" or give a
concrete example (or a link) to illustrate it ?


Thanks,
Serge.

jimmy frasche

unread,
Feb 6, 2011, 3:13:33 AM2/6/11
to Serge Hulne, golang-nuts
Composition means has-a. Instead of inheriting from a
LoggingAbstractFactoryDecoratorLampshade2 you just have a private
Logger member.

Serge Hulne

unread,
Feb 6, 2011, 3:55:28 AM2/6/11
to golang-nuts

On Feb 6, 9:13 am, jimmy frasche <soapboxcic...@gmail.com> wrote:
> Composition means has-a. Instead of inheriting from a
> LoggingAbstractFactoryDecoratorLampshade2 you just have a private
> Logger member.
>

Hi Jimmy,


Do you mean, by this:

"Given a structure A (with it's members and methods), in order to add
functionality to A, a typical Go solution would consist in embedding
another structure B (which is supposed to provide said additional
functionality), as a private member, in the initial structure A ?"

Is the expected effect of said composition the following one :

"If structure A implements an interface IA and B implements an
interface IB, then embedding B into A (making B a private member of
A), would result in the "composed" structure being "extended" in that
it implements both IA and IB" interfaces.


Serge.

chris dollin

unread,
Feb 6, 2011, 4:09:22 AM2/6/11
to Serge Hulne, golang-nuts
On 5 February 2011 22:28, Serge Hulne <serge...@gmail.com> wrote:

> Say, for instance, you modify the container/vector.go module at some
> point, then

... the changes are propagated to the int/string/whatever vector
files as part of the build.

> (Because of the mandatory duplication of code,

Go has functions. Go has /first-class/ functions, which means
you can store [pointers to] them in data structures and
imitate method over-riding if you have to think that way.
Which you don't.

> When studying C++, Java and Python it struck me that one very strong
> point that all thee languages have in common is their build-in ability
> to map the (inheritance-related) tree structures of classes which
> represent real-world objects which by nature possess hierarchical
> relationships,

No, they don't "by nature" have hierarchical relationships; we impose
those relationships in our modelling. Different modelling imposes
different constraints.

There's an (implicit) is-a graph (NOT a hierarchy) among Go's
interfaces which IMAO is where it belongs. Trying to conflate the
descriptive is-a's with inheritance is-subclass-of (aka extends)
doesn't seem to work very well.

This is I think another case of puddings, the proof of which is
to be gained by trying out coding tactics allowed by the language
and seeing how Go's approach works on a larger scale than
that of class & method declarations.

Serge Hulne

unread,
Feb 6, 2011, 4:43:11 AM2/6/11
to golang-nuts
Hi Chris,

>
> There's an (implicit) is-a graph (NOT a hierarchy) among Go's
> interfaces which IMAO is where it belongs.
>


This is probably the part that has been eluding me so far.


Could you please provide me with a link (or a hint) to a part of the
code (or the doc) that illustrates this particular concept ?

Thank you for your help, I really appreciate it !
Serge.

chris dollin

unread,
Feb 6, 2011, 5:03:39 AM2/6/11
to Serge Hulne, golang-nuts
On 6 February 2011 09:43, Serge Hulne <serge...@gmail.com> wrote:
> Hi Chris,
>
>> There's an (implicit) is-a graph (NOT a hierarchy) among Go's
>> interfaces which IMAO is where it belongs.
>
> This is probably the part that has been eluding me so far.
>
> Could you please provide me with a link (or a hint) to a part of the
> code (or the doc) that illustrates this particular concept ?

http://golang.org/doc/go_spec.html#Interface_types

should be enough to get started. A variable (so, also a
function argument) of interface type T can accept a value
of any type U that implements at least the methods of T.
If U is also an interface type, you can think of U being a
subinterface of T.

It's not a hierarchy because different superinterfaces of U
might omit different methods. But there is a unique top
element, viz interface{}.

Serge Hulne

unread,
Feb 7, 2011, 2:35:00 AM2/7/11
to golang-nuts


On Feb 6, 9:13 am, jimmy frasche <soapboxcic...@gmail.com> wrote:
> Composition means has-a. Instead of inheriting from a
> LoggingAbstractFactoryDecoratorLampshade2 you just have a private
> Logger member.
>

Or you simply add a corresponding method to the object in question ?

Serge.

Serge Hulne

unread,
Feb 7, 2011, 2:45:14 AM2/7/11
to golang-nuts

On Feb 6, 12:22 am, jimmy frasche <soapboxcic...@gmail.com> wrote:
> How could inheritance cut down any of the code in container/vector?
> (which has been said is generated automatically, not by hand, re
> maintenance nightmares)
>
>

Is that not a contradiction in terms :


- If the mandatory duplication of code (between the modules : vector,
intvector, and stringvector) is "generated automatically" (by an
external script, during the build), then is that not the very proof
that the issue is not handled by the language ?


Serge.

jimmy frasche

unread,
Feb 7, 2011, 2:50:54 AM2/7/11
to Serge Hulne, golang-nuts
On Sun, Feb 6, 2011 at 11:45 PM, Serge Hulne <serge...@gmail.com> wrote:
> - If the mandatory duplication of code (between the modules : vector,
> intvector, and stringvector) is "generated automatically" (by an
> external script, during the build), then is that not the very proof
> that the issue is not handled by the language ?

Yes, one that could be solved by allowing paramaterization of types,
but I do not see how it could be solved by inheritance.

As for your last question, I don't really follow it. There are lots of
articles on the internet about composition vs. inheritance that could
be much more informative than I could be.

roger peppe

unread,
Feb 7, 2011, 2:57:46 AM2/7/11
to Serge Hulne, golang-nuts
On 7 February 2011 07:45, Serge Hulne <serge...@gmail.com> wrote:
> - If the mandatory duplication of code (between the modules : vector,
> intvector, and stringvector) is "generated automatically" (by an
> external script, during the build), then is that not the very proof
> that the issue is not handled by the language ?

nobody is saying that this particular issue is handled by the language,
which is why generics are still being considered.

on the other hand, the vector types are unusual in that the underlying
data structure is visible as well as the methods on it (which is why
it matters that StringVector actually works on []string rather than
[]interface{}).

for other kinds of generic data structure, using interface{} and
defining methods to transform to and from the required data
type can work, with a relatively small runtime cost.

bflm

unread,
Feb 7, 2011, 3:05:46 AM2/7/11
to golang-nuts
On Feb 7, 8:45 am, Serge Hulne <serge.hu...@gmail.com> wrote:
> - If the mandatory duplication of code (between the modules : vector,
> intvector, and stringvector) is "generated automatically" (by an
> external script, during the build), then is that not the very proof
> that the issue is not handled by the language ?
Congrats for inventing a "proof" of a well known property of the Go
language (though not an *issue*). It is BTW the first item one can see
in the "Conceptual Differences" here:
http://golang.org/doc/go_for_cpp_programmers.html

Also, this topic has been discussed here countless times before,
please search the list.

Serge Hulne

unread,
Feb 7, 2011, 3:17:14 AM2/7/11
to golang-nuts


>
> on the other hand, the vector types are unusual in that the underlying
> data structure is visible as well as the methods on it (which is why
> it matters that StringVector actually works on []string rather than
> []interface{}).
>
> for other kinds of generic data structure, using interface{} and
> defining methods to transform to and from the required data
> type can work, with a relatively small runtime cost.
>

So basically, there are two ways to handle the case of closely related
data structure in Go (as in the example of the vectors):

. 1 : Duplicate the code and slightly modify it to fit every single
particular case (as in vector, intvector and stringvector).

(tedious, but efficient)

or

. 2 : Use reflection to deal with the actual type of the variable(s)
(at runtime).

(more elegant, less error-prone (no duplication), less maintenance-
intensive (for the same reason), but possibly slower at run time)

Serge.

chris dollin

unread,
Feb 7, 2011, 3:28:22 AM2/7/11
to Serge Hulne, golang-nuts
On 7 February 2011 08:17, Serge Hulne <serge...@gmail.com> wrote:

> So basically, there are two ways to handle the case of closely related
> data structure in Go (as in the example of the vectors):
>
> . 1 : Duplicate the code and slightly modify it to fit every single
> particular case (as in vector, intvector and stringvector).

Not /every/ single particular case. Enough to be useful.

> (tedious, but efficient)

Tedious the first time; then you write a program in Your Preferred
Language to do the job.

> or
>
> . 2 : Use reflection to deal with the actual type of the variable(s)
> (at runtime).
>
> (more elegant,

Hmm, hardly "elegant" IMAO.

> less error-prone (no duplication),

Playing with reflection is non-trivial.

> less maintenance-
> intensive (for the same reason), but possibly

I would have said "almost certainly", but I defer to the Go
implementors.

> slower at run time)

Or: pick a good choice of iterfaces (eg the sort interface, which
doesn't do either of your suggestions).

Serge Hulne

unread,
Feb 7, 2011, 3:42:01 AM2/7/11
to golang-nuts

My concluding remark:

In the absence of inheritance or generics, extension/specialization
can still be handled in Go, through interfaces, composition (and
reflection).

So there is still hope that I will be able to manage to port some
Python code to Go, not via a mere trivial mapping, but by transposing
the typical Python constructs (which make extensive use of inheritance
and overloading) into valid, efficient Go code, by translating it in
terms of interfaces, composition, and reflection.

Thanks to all those who took the time and had the patience to answer
the questions which were raised in this thread !

I sincerely believe that these answers will prove very valuable to all
those who are planning to migrate code from Java, C++ or Python to Go.

Serge.

roger peppe

unread,
Feb 7, 2011, 5:00:01 AM2/7/11
to Serge Hulne, golang-nuts
On 7 February 2011 08:17, Serge Hulne <serge...@gmail.com> wrote:
> or
>
> . 2 : Use reflection to deal with the actual type of the variable(s)
> (at runtime).
>
> (more elegant, less error-prone (no duplication), less maintenance-
> intensive (for the same reason), but possibly slower at run time)

i'm not sure that "reflection" is the right term for what i was referring to.

in my view reflection involves writing code to inspect the type
itself (with the reflect package) not simply converting between
dynamic and static types.

it's worth making the distinction, because converting between dynamic
and static types is straightforward, quite efficient and often used, whereas
using reflection is harder, not nearly as efficient and is used
rarely.

good luck with the python port!

David Roundy

unread,
Feb 7, 2011, 10:39:12 AM2/7/11
to Serge Hulne, golang-nuts
On Mon, Feb 7, 2011 at 12:17 AM, Serge Hulne <serge...@gmail.com> wrote:
> So basically, there are two ways to handle the case of closely related
> data structure in Go (as in the example of the vectors):
>
> . 1 : Duplicate the code and slightly modify it to fit every single
> particular case (as in vector, intvector and stringvector).
>
> (tedious, but efficient)
>
> or
>
> . 2 : Use reflection to deal with the actual type of the variable(s)
> (at runtime).
>
> (more elegant, less error-prone (no duplication), less maintenance-
> intensive (for the same reason), but possibly slower at run time)

I'd hardly say "less error-prone", since it (in general) means that
many errors become runtime errors rather than compile-time errors.
Unless you have sufficiently simple code that it can be expressed as a
set of interface methods. But it would depend on whether this is
write-once-use-many-times sort of code or the inverse. Certainly
StringVector falls in the category of code that is seldom written but
often used, so having a type-safe API is definitely invaluable.
--
David Roundy

Steven

unread,
Feb 8, 2011, 11:44:39 PM2/8/11
to David Roundy, Serge Hulne, golang-nuts
Just as an addendum, I had an idea stuck in my head and needed to stick it on paper. Its basically a way of modelling inheritance and inheritance related behaviour in Go using a form of traits. I'm posting it because its related to this topic. Please be kind, as it is very rough and was made mostly for my own benefit. Its the simplest general model I could think of, unless someone else has a simpler one. This is by no means a suggestion as to how to write good Go code, merely a tool for when one's brain is a bit clogged with OOPwebs ;-)

bflm

unread,
Feb 9, 2011, 2:36:59 AM2/9/11
to golang-nuts
On Feb 9, 5:44 am, Steven <steven...@gmail.com> wrote:
> Please be kind, as it is very rough and was made
> mostly for my own benefit.
I don't want to be unkind, but IMO there's no benefit. The
xenotransplantation Go is undergoing under your treatment hurts you
both.
> https://docs.google.com/document/pub?id=1jvHSajl_GaEzs_7ukHpUiqlnxBpU...

Serge Hulne

unread,
Feb 9, 2011, 3:41:24 AM2/9/11
to golang-nuts
Hi all,

From what I gather from all the advice that has been given to me in
this thread, the most efficient way (and also the one fitting the most
closely the Go paradigm) in order to port my Python code (which
heavily relies on specialization, i.e. inheritance) to Go is to
transpose all the relationships expressed in terms of classes and
inheritance in terms of interfaces (and just forget about inheritance
altogether).

I must confess that the whole thread started from a misunderstanding
on my part: I have been studying Go for about a year now and although
I understand the concept of interface, as such, I thought that perhaps
Go *also* provided a way to implement some kind of inheritance (but
that its modus operandi eluded me).

The starting point of this misunderstanding is that some other
languages I have used in the past (like e.g Java) support both classes
(hence inheritance) *and* interfaces.

Serge.

chris dollin

unread,
Feb 9, 2011, 4:04:43 AM2/9/11
to Serge Hulne, golang-nuts
On 9 February 2011 08:41, Serge Hulne <serge...@gmail.com> wrote:

> I must confess that the whole thread started from a misunderstanding
> on my part: I have been studying Go for about a year now and although
> I understand the concept of interface, as such, I thought that perhaps
> Go *also* provided a way to implement some kind of inheritance (but
> that its modus operandi eluded me).

Embedding provides "some kind of inheritance", where "some kind of"
excludes method over-riding in the Java style; perhaps that's what
provoked your misunderstanding?

That is, if type T has methods M1, M2 ..., then given

type U struct { T; other Something }

U has the methods of T and any other methods that get defined on U.
And you can redefine methods from T.

HOWEVER if M1 of T calls M2 of T, and U has a redefinition of M2,
calling M1 of U calls M1 of T, NOT of U. That's what I mean above
by "excludes method over-riding". (Methods of) T don't get to see
redefinitions made in types that embed T.

(If one wants an effect similar to that, you can deploy Go's
existing machinery to do something like it; but you have to
pay more up-front in T and elsewhere for doing so.)

Steven

unread,
Feb 9, 2011, 10:46:23 AM2/9/11
to bflm, golang-nuts

But here's the thing: this isn't actually a "xenotransplantation" as
you put it :-). It uses the very common go idiom of composing
behaviour by combining discrete layers (see Writer and Reader). It
takes a bunch of related end behaviours acting on the same object and
groups them together (also not new, see ReadWriter). The only thing is
that it is self referential, which allows you to combine the various
elements in parallel, rather than in series.

People often come to Go with a very strong tendency to follow some
form of inheritance model. They either get frustrated by people saying
"no you can't do that here" and leave, or they attempt to emulate it
directly using embedding for inheritance, and interfaces for vtables.
This makes for very messy, inflexible, and all around *bad* code.
Sometimes (though rarely) inheritance *is* the optimal way to express
the relationship between a set of types, and people either try the
afore mentioned horror code, or give up and use another language.
Often, if you think about it hard enough, you can come up with a
clever alternate representation that is specific to your use. However,
this isn't always forthcoming.

While I'm not claiming that this is the be all and end all of Go
design (far from it!), it does allow you to start modelling what you
have in your head when it doesn't fit Go's standard abstractions. And
what happens when you realize that you don't need the inheritance
crutch? You have already written easily composible types, useful
interfaces, and a library of proceedural functions which should work
just swell, with a minimum of extra code wasted on beating it into the
now abandoned inheritance abstraction.

I probably didn't do it justice, either by my explanation of it (sorry
it was abstract, I usually have to do many revisions to clarify
things), or by presenting it as an inheritance strategy rather than
what it really is: an very general purpose scheme for parallel
composition. However, I was a bit excited about how much simpler it is
than most attempts at inheritance-like schemes in Go and felt I should
draw attention to how it can be used for this purpose. Of course, if
there's a specific glaring reason why this is *bad* (apart from
enabling people to model inheritance), then feel free to point it out.
I haven't come up with one yet.

Benny Siegert

unread,
Feb 11, 2011, 8:27:20 AM2/11/11
to Serge Hulne, golang-nuts
On Sun, Feb 6, 2011 at 10:43, Serge Hulne <serge...@gmail.com> wrote:
>> There's an (implicit) is-a graph (NOT a hierarchy) among Go's
>> interfaces which IMAO is where it belongs.
>
> This is probably the part that has been eluding me so far.
>
> Could you please provide me with a link (or a hint) to a part of the
> code (or the doc) that illustrates this particular concept ?

It is very simple actually. If you look at the io package, for
example, there is an interface called Reader, which contains a Read
method, and a Seeker with a Seek method. Then there is the ReadSeeker:

type ReadSeeker interface {
Reader
Seeker
}

This is composition, but it serves to illustrate an is-a relationship:
a ReadSeeker is also a Reader and a Seeker. If you have a type that
implements ReadSeeker, it will also implement Reader and Seeker.

--Benny.

Reply all
Reply to author
Forward
0 new messages