How would this abstraction work in Go?

88 views
Skip to first unread message

Robert B.

unread,
Nov 17, 2009, 8:51:35 PM11/17/09
to golang-nuts
Suppose I have a struct which defines lots of methods, including this
one:

type Base struct {
}

func (this *Base) DoSomething(other *Base) int {
if this.Cross(other) == 0 {
return DoSomethingComplicated(this, other)
}
return DoSomethingElseComplicated(this, other)
}

However, the definition of Cross is left out of Base. Only the
DoSomething logic is implemented. Other structs with Base embedded
would implement Cross the way they want. In Java, Base would be an
abstract class, and it would declare a method Cross to be abstract.

Now, it seems to me that this is exactly what interfaces are for:
DoSomething should have a receiver that implements Cross, and take an
argument that implements Cross. But you can't have methods on
interfaces. Furthermore, since Base does not implement Cross,
DoSomething will not even compile.

I could try this:

type BaseInterface interface {
DoSomething(other *BaseInterface) int;
Cross(other *BaseInterface) bool;
}

type Base struct {
BaseInterface;
}

func (this *Base) DoSomething(other *BaseInterface) int ....

And I could leave Cross unimplemented. That compiles. Is that the Go
way to do this? Apparently I can also create a new Base, and neither
the compiler nor the runtime gives an error, although it seems to be
chance whether accessing Base will segfault or not.

Thanks,

--Rob

Peter Froehlich

unread,
Nov 17, 2009, 8:57:13 PM11/17/09
to Robert B., golang-nuts
Turn Cross into an interface and pass an instance implementing that to
whatever function creates your Base instances.
--
Peter H. Froehlich <http://www.cs.jhu.edu/~phf/>
Senior Lecturer | Director, Johns Hopkins Gaming Lab

Russ Cox

unread,
Nov 18, 2009, 1:33:52 AM11/18/09
to Robert B., golang-nuts
On Tue, Nov 17, 2009 at 17:51, Robert B. <auto...@gmail.com> wrote:
> Suppose I have a struct which defines lots of methods, including this
> one:
>
> type Base struct {
> }
>
> func (this *Base) DoSomething(other *Base) int {
>    if this.Cross(other) == 0 {
>        return DoSomethingComplicated(this, other)
>    }
>    return DoSomethingElseComplicated(this, other)
> }

This is a Java pattern and does not translate well into Go,
just as many Go patterns do not translate well into Java.
A better question is what you're really trying to do, so that
we can find what the Go idiom would be.

Not really understanding the situation, it seems to me
that you could just use an ordinary function:

type I interface {
Cross(I) int;
Thing1(I);
Thing2(I);
}

func DoSomething(i, j I) {
if i.Cross(j) == 0 {
i.Thing1(j);
} else {
i.Thing2(j);
}
}

This is similar to io.ReadFull, which takes an io.Reader
and uses its methods (well, method). In Java or C++ maybe the
reader base class would have an abstract Read and a concrete
ReadFull; in Go, ReadFull is just its own standalone function.

Russ

Robert B.

unread,
Nov 18, 2009, 9:34:07 AM11/18/09
to r...@golang.org, golang-nuts
Well, the goal here is to allow a user to create their own struct which conforms to a particular interface. However, I provide most of the methods (which may call missing methods), but it is up to the user to complete the rest. The application can then use the user's struct because it implements that interface.

What would the appropriate Go pattern look like?

Thanks!

--Rob

Peter Froehlich

unread,
Nov 18, 2009, 10:07:56 AM11/18/09
to Robert B., r...@golang.org, golang-nuts
Hi all,

On Wed, Nov 18, 2009 at 9:34 AM, Robert B. <auto...@gmail.com> wrote:
> Well, the goal here is to allow a user to create their own struct which
> conforms to a particular interface. However, I provide most of the methods
> (which may call missing methods), but it is up to the user to complete the
> rest. The application can then use the user's struct because it implements
> that interface.

I said it before, but let me say it again, more slowly. Take all the
methods the user has to implement and put them into an interface, call
it "UserImplemented". Let's assume your struct is called
"RobertImplemented". Make a field in your struct:

...
userStuff UserImplemented;
...

In the code of "RobertImplemented" where you want to call the pieces
of code from the users, simply say "self.userStuff.SomeMethod(...)" to
do the calls. Finally either take an instance of UserImplemented in
your factory, or provide a method for the user to register their
implementation (not as safe).

How does this not solve your problem?

Cheers,
Peter

Robert B.

unread,
Nov 18, 2009, 10:42:02 AM11/18/09
to golang-nuts
Well, what if my user wrote an entire library based on his own struct
which implemented UserImplemented. Say it looked like this:

type struct MyImplementation {
}

... funcs which satisfy the UserImplemented interface
... funcs which go beyond the interface

To construct a RobertImplemented struct, there would be a factory
method in RobertImplemented which takes a UserImplemented and gives
back a RobertImplemented. So far, so good. However, now I can't call
the MyImplementation funcs (via RobertImplemented methods or other
functions which take RobertImplemented values) which go beyond the
UserImplemented interface because I have a RobertImplemented struct,
not a MyImplementation struct.

I realize that I'm still thinking in Java patterns... I've been
exclusively programming in Java ever since 1.1, and before that in C+
+, so it's proving a little hard to move to a language that is sorta-
kinda "yes and no" object oriented (see FAQ) ;)

Thanks for your patient help!

--Rob

On Nov 18, 10:07 am, Peter Froehlich <peter.hans.froehl...@gmail.com>
wrote:
> Hi all,

roger peppe

unread,
Nov 18, 2009, 11:30:08 AM11/18/09
to Robert B., golang-nuts
2009/11/18 Robert B. <auto...@gmail.com>:
> Well, what if my user wrote an entire library based on his own struct
> which implemented UserImplemented. Say it looked like this:
>
> type struct MyImplementation {
> }
>
> ... funcs which satisfy the UserImplemented interface
> ... funcs which go beyond the interface
>
> To construct a RobertImplemented struct, there would be a factory
> method in RobertImplemented which takes a UserImplemented and gives
> back a RobertImplemented.

given that MyImplementation satisfies the UserImplemented interface,
why do you need a RobertImplemented at all?
(i could ask that the other way around too).

Ben Tilly

unread,
Nov 18, 2009, 11:33:46 AM11/18/09
to roger peppe, Robert B., golang-nuts
He's trying to emulate inheriting from an abstract class in Java. You
do this so that you can implement several very similar classes without
having to duplicate code.

Cheers,
Ben

Ben Tilly

unread,
Nov 18, 2009, 11:30:39 AM11/18/09
to Robert B., golang-nuts
On Wed, Nov 18, 2009 at 7:42 AM, Robert B. <auto...@gmail.com> wrote:
> Well, what if my user wrote an entire library based on his own struct
> which implemented UserImplemented. Say it looked like this:
>
> type struct MyImplementation {
> }

I believe that MyImplementation should come before struct.

> ... funcs which satisfy the UserImplemented interface
> ... funcs which go beyond the interface
>
> To construct a RobertImplemented struct, there would be a factory
> method in RobertImplemented which takes a UserImplemented and gives
> back a RobertImplemented. So far, so good. However, now I can't call
> the MyImplementation funcs (via RobertImplemented methods or other
> functions which take RobertImplemented values) which go beyond the
> UserImplemented interface because I have a RobertImplemented struct,
> not a MyImplementation struct.

One approach to accomplish this is:

type MyInterface interface {
// List methods here
}

type MyImplementation struct {
self MyInterface;
}

// Implement MyImplementation

type RobertImplementation struct {MyImplementation}

// Implement more methods

When you construct a new object you have to remember to set obj.self =
obj. And then within all of the methods in MyImplementation and
RobertImplementation you can obj.self.method() to dynamically do what
you want.

> I realize that I'm still thinking in Java patterns... I've been
> exclusively programming in Java ever since 1.1, and before that in C+
> +, so it's proving a little hard to move to a language that is sorta-
> kinda "yes and no" object oriented (see FAQ)  ;)

I suspect a lot of people will have trouble with that.

Cheers,
Ben

Robert B.

unread,
Nov 18, 2009, 1:10:43 PM11/18/09
to Ben Tilly, roger peppe, golang-nuts
On Wed, Nov 18, 2009 at 11:33 AM, Ben Tilly <bti...@gmail.com> wrote:
He's trying to emulate inheriting from an abstract class in Java.  You
do this so that you can implement several very similar classes without
having to duplicate code.
 
Right, exactly. I have a RobertImplementation, and a MyImplementation. Any method in the library that takes a RobertImplementation should be able to take a MyImplementation as well (which self.MyImplementation.method() solves).

But also, any method in the library that requires a MyImplementation method should also be able to call RobertImplementation methods, because through my Java Goggles, a MyImplementation is a RobertImplementation. Thusly:

type UserRequiredInterface interface {
   Func1();
   Func2();
}

type RobertImplementation struct {
   userImpl UserRequiredInterface;
}

func (this *RobertImplementation) FuncA() { ... }
func (this *RobertImplementation) FuncB() { ... }

FuncA() could call Func1() via userImpl.Func1().

// And then the user does this:

type MyImplementation struct {
}

func (this *MyImplementation) Func1() { ... }
func (this *MyImplementation) Func2() { ... }

Now, Func2() might need to call FuncA() or FuncB(). What would the Go pattern be here?

--Rob


Ben Tilly

unread,
Nov 18, 2009, 1:44:11 PM11/18/09
to Robert B., roger peppe, golang-nuts
On Wed, Nov 18, 2009 at 10:10 AM, Robert B. <auto...@gmail.com> wrote:
> On Wed, Nov 18, 2009 at 11:33 AM, Ben Tilly <bti...@gmail.com> wrote:
>>
>> He's trying to emulate inheriting from an abstract class in Java.  You
>> do this so that you can implement several very similar classes without
>> having to duplicate code.
>
>
> Right, exactly. [...]
>
> Now, Func2() might need to call FuncA() or FuncB(). What would the Go
> pattern be here?

Did you see my other email that demonstrated how to do this?

Cheers,
Ben

Peter Froehlich

unread,
Nov 18, 2009, 2:27:25 PM11/18/09
to Robert B., Ben Tilly, roger peppe, golang-nuts
On Wed, Nov 18, 2009 at 1:10 PM, Robert B. <auto...@gmail.com> wrote:
> Now, Func2() might need to call FuncA() or FuncB(). What would the Go
> pattern be here?

If you want to call methods in what in Java would be your "base class"
you need to explicitly pass a "base class" instance to Func2. Really
what you're doing is "call backs" and instead of munging that together
in some default way like inheritance in Java does, everything is
explicit in Go.

Darn, I think I am about to become Go marketing flak. :-D

Robert B.

unread,
Nov 18, 2009, 3:53:34 PM11/18/09
to Peter Froehlich, Ben Tilly, roger peppe, golang-nuts
Ok, I think I'm getting the purpose of RobertImplementation and MyImplementation totally confused. Let's stick with CommonImplementation and FullImplementation (realizing that each user may write any FullImplementation).

type CommonMethods interface {
    FuncA();
    FuncB();
}

// A user implements these
type RequiredMethods interface {
    Func1();
    Func2();
}

type CommonImplementation struct {
    userImpl RequiredMethods;
}

// FuncA() and FuncB() may call anything in userImpl.
func (this *CommonImplementation) FuncA() { ... }
func (this *CommonImplementation) FuncB() { ... }

type FullImplementation struct {
    self *CommonImplementation; // Ben's requirement
    // CommonImplementation; // why not just embed like this?
}

// Func1() and Func2() may call anything in CommonImplementation
// or FullImplementation.
func (this *FullImplementation) Func1() { ... }
func (this *FullImplementation) Func2() { ... }


My question is, is the above the correct Go idiom that should be used when rethinking abstract base types from Java?

Thanks,

--Rob

Ben Tilly

unread,
Nov 18, 2009, 4:29:32 PM11/18/09
to Robert B., Peter Froehlich, roger peppe, golang-nuts
On Wed, Nov 18, 2009 at 12:53 PM, Robert B. <auto...@gmail.com> wrote:
> Ok, I think I'm getting the purpose of RobertImplementation and
> MyImplementation totally confused. Let's stick with CommonImplementation and
> FullImplementation (realizing that each user may write any
> FullImplementation).

OK.


> type CommonMethods interface {
>     FuncA();
>     FuncB();
> }
> // A user implements these
> type RequiredMethods interface {
>     Func1();
>     Func2();
> }

No. You want this:

type FullMethods interface {
FuncA();
FuncB();

// A user implements these
Func1();
Func2();
}

> type CommonImplementation struct {
>     userImpl RequiredMethods;
> }

type CommonImplementation struct {
self FullMethods; // Or call this userImpl if you want
}

> // FuncA() and FuncB() may call anything in userImpl.

Actually in FullMethods. Which means that they can make method calls
to FullImplementation methods that are not implemented in
CommonImplementation. The key here for polymorphism is that a method
call to the CommonImplementation doesn't lose the full object because
the full object is still embedded in self.

> func (this *CommonImplementation) FuncA() { ... }
> func (this *CommonImplementation) FuncB() { ... }
> type FullImplementation struct {
>     self *CommonImplementation; // Ben's requirement
>     // CommonImplementation; // why not just embed like this?
> }

The second embedding is the one to use. My suggestion put self inside
the CommonImplementation. Putting self inside of FullImplementation
means that you lose the full object when you call the base class.
That prevents you from, for instance, being able to see that the child
has overridden a method.

> // Func1() and Func2() may call anything in CommonImplementation
> // or FullImplementation.
> func (this *FullImplementation) Func1() { ... }
> func (this *FullImplementation) Func2() { ... }
>
> My question is, is the above the correct Go idiom that should be used when
> rethinking abstract base types from Java?

With the significant revisions I described this Go idiom will let you
port a design with abstract base types to Go. However discovering
alternate designs that are more natural for Go will often result in
simpler code that performs better.

Cheers,
Ben

Robert B.

unread,
Nov 18, 2009, 4:51:31 PM11/18/09
to Ben Tilly, Peter Froehlich, roger peppe, golang-nuts
I understand it now, thanks. I'm not quite up to the point where I can "think in Go" yet.

Thanks again,

--Rob
Reply all
Reply to author
Forward
0 new messages