Thinking OO virtual function in Go

417 views
Skip to first unread message

Tong Sun

unread,
Nov 22, 2016, 4:16:27 PM11/22/16
to golang-nuts
Hi, 

How to architect the OO's virtual function in Go? 

Please take a look at this (not working) Go program

Please think of the "func Output()" as a very complicated function that I only want to define once at the base level, not to duplicate into each sub classes. 

How can I make it works so that the last output statement, instead of being, 

fmt.Printf("[%v] %s: [%0.2f]\n", k, v.Name(), v.Area())

will be this instead:

fmt.Printf("[%v] %s\n", k, v.Output())

Thanks

Jesse McNelis

unread,
Nov 22, 2016, 4:29:25 PM11/22/16
to Tong Sun, golang-nuts
You define a function:

func Output(s Shape) string {
return s.Name() + "'s area size is " + s.Area()
}

Go uses interfaces for polymorphism.
Other OOP languages can use inheritance for polymorphism too, but Go
doesn't have inheritance.

Seb Binet

unread,
Nov 22, 2016, 4:30:20 PM11/22/16
to Tong Sun, golang-nuts

Tong Sun

unread,
Nov 22, 2016, 4:34:16 PM11/22/16
to Seb Binet, golang-nuts


On Tue, Nov 22, 2016 at 4:29 PM, Seb Binet wrote:



On Tue, Nov 22, 2016 at 10:16 PM, Tong Sun wrote:
Hi, 

How to architect the OO's virtual function in Go? 

Please take a look at this (not working) Go program

Please think of the "func Output()" as a very complicated function that I only want to define once at the base level, not to duplicate into each sub classes. 

How can I make it works so that the last output statement, instead of being, 

fmt.Printf("[%v] %s: [%0.2f]\n", k, v.Name(), v.Area())

will be this instead:

fmt.Printf("[%v] %s\n", k, v.Output())

what about this:


Aha, that way. I've been banning my head for a while & you saved my day. Thanks a lot!

Tong Sun

unread,
Nov 22, 2016, 5:03:54 PM11/22/16
to Jesse McNelis, golang-nuts


On Tue, Nov 22, 2016 at 4:29 PM, Jesse McNelis wrote:

Thanks Jesse. That works. 

However I just realized that it is only part of my problem. 

I have a huge list of common variables that I defined in my "base class", changing it from a member function to a pure function causes almost every single variable now undefined. 

I can demonstrate the problem with this code

So, once again, thinking in OO, I'll define all of the common variables in base class, and common functionalities in virtual functions. How to make that idea work in Go?

For the above specific code, how to easily make "func Output" works?

Thanks

Jesse McNelis

unread,
Nov 22, 2016, 6:23:41 PM11/22/16
to Tong Sun, golang-nuts

On 23 Nov. 2016 9:03 am, "Tong Sun" <sunto...@gmail.com> wrote:
>
> So, once again, thinking in OO, I'll define all of the common variables in base class, and common functionalities in virtual functions. How to make that idea work in Go?
>
> For the above specific code, how to easily make "func Output" works?
>

You can use functions and embedding for code reuse and interfaces for polymorphism.

In your example you've implemented Speak() method for each type and defined a Speaker interface but then in Output() you don't call Speak().

If you have fields you want available through the Speaker interface then you need to define getter methods for those fields and add them to the interface.

You could also add a Speak() method to the Animal type which would then be available on any type that embeds an Animal and would have direct access to any fields on Animal.

You could define a Speak() function that all the Speak() methods call for some common functionality.

How you structure it depends on what real world problem you're trying to solve.


Nick Patavalis

unread,
Nov 22, 2016, 6:25:21 PM11/22/16
to golang-nuts, jes...@jessta.id.au
Hi,

There is no direct mapping of what you can do with virtual functions in other OO languages, and Go. There are different compromises you have to make; because of this, synthetic examples will probably not help much. 

That being said, in the case of your last example I would make Output() a method of Animal. 
The Speak() methods of the specific animals would then call Output().

/npat

parais...@gmail.com

unread,
Nov 22, 2016, 6:27:16 PM11/22/16
to golang-nuts, jes...@jessta.id.au
interfaces only work on methods. 

https://play.golang.org/p/o6Ot4IdJZ1

Name, Age , ... are not methods they are fields.You need to make them part of Speaker interface by using methods instead of fields.

Tong Sun

unread,
Nov 22, 2016, 11:04:34 PM11/22/16
to Jesse McNelis, golang-nuts


On Tue, Nov 22, 2016 at 6:23 PM, Jesse McNelis wrote:

On 23 Nov. 2016 9:03 am, "Tong Sun" wrote:
>
> So, once again, thinking in OO, I'll define all of the common variables in base class, and common functionalities in virtual functions. How to make that idea work in Go?
>
> For the above specific code, how to easily make "func Output" works?
>

You can use functions and embedding for code reuse and interfaces for polymorphism.

In your example you've implemented Speak() method for each type and defined a Speaker interface but then in Output() you don't call Speak().

Yeah, that Output() is the virtual function I'm implementing. In my real code it has nothing to do with Speak() method, only to use those common variables. 

If you have fields you want available through the Speaker interface then you need to define getter methods for those fields and add them to the interface.

Got it.  

You could also add a Speak() method to the Animal type which would then be available on any type that embeds an Animal and would have direct access to any fields on Animal.

However, different animals speak differently. I.e., it is not the fields on Animal that matter but what outside it matter to Speak(), right? So adding a Speak() method to the Animal doesn't make much sense, right?
 

You could define a Speak() function that all the Speak() methods call for some common functionality.

How you structure it depends on what real world problem you're trying to solve.

Probably, but just thinking in abstract terms, I thought there are some basic principles that people can follow. Anyway, I think paraiso.marc's methodology might be the only way. 


Tong Sun

unread,
Nov 22, 2016, 11:08:00 PM11/22/16
to Nick Patavalis, golang-nuts, Jesse McNelis
No Nick, making Output() a member method won't work. 
See my OP and Jesse's answer. I.e., I have to change it from a member function to a pure function. 

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/f_62HEOIBV4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Tong Sun

unread,
Nov 22, 2016, 11:11:15 PM11/22/16
to parais...@gmail.com, golang-nuts, Jesse McNelis
Thanks a lot for your explicit example. Much more helpful to me than merely saying define getters. Much appreciate it! 

Nick Patavalis

unread,
Nov 23, 2016, 4:05:31 AM11/23/16
to golang-nuts, nick.pa...@gmail.com, jes...@jessta.id.au
Hi,

In your *second* example, making Output() a method of Animal will work, since it uses only the members (fields) of Animal, and not the fields of specific animals (or any behavior that varies between animals). That's why I'm insisting on *real* and *specific* examples, not synthetic ones.

/npat
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.

Tahir Hashmi

unread,
Nov 23, 2016, 8:52:55 AM11/23/16
to golang-nuts
I've tried to derive how to achieve a) implementation inheritance, followed by b) type substitution and c) enabling dynamic dispatch as in virtual functions in my blog post here: https://tech.t9i.in/2014/01/22/inheritance-semantics-in-go/

I also like the approach suggested by Sebastien Binet. It's really neat in situations where you can get by without needing to override the base implementation (e.g. of Shape.Output()).

Tong Sun

unread,
Nov 23, 2016, 9:34:59 AM11/23/16
to parais...@gmail.com, golang-nuts, Jesse McNelis
Further on that, apart from the getters, is it possible to define a left-value function, so that I can use it to update the member values? E.g., from https://play.golang.org/p/jlgIFjI268,

func (a *Animal) TheAge() int { return a.Age }
or
func (a *Animal) TheAge() *int { return &a.Age }

*d.TheAge() += 1
*d.TheAge() *= 2

What is the canonical way to do it? 

Tong Sun

unread,
Nov 23, 2016, 9:35:52 AM11/23/16
to Nick Patavalis, golang-nuts
Have you noticed the IsA() func call there?

To unsubscribe from this group and all its topics, send an email to golang-nuts+unsubscribe@googlegroups.com.

Tong Sun

unread,
Nov 23, 2016, 9:51:23 AM11/23/16
to Tahir Hashmi, golang-nuts
On Wed, Nov 23, 2016 at 8:52 AM, Tahir Hashmi wrote:

I also like the approach suggested by Sebastien Binet. It's really neat in situations where you can get by without needing to override the base implementation (e.g. of Shape.Output()).
 
That it is only a partial solution -- I have a huge list of common variables that I defined in my "base class", changing it from a member function to a pure function causes almost every single variable now undefined. 

I've tried to derive how to achieve a) implementation inheritance, followed by b) type substitution and c) enabling dynamic dispatch as in virtual functions in my blog post here: https://tech.t9i.in/2014/01/22/inheritance-semantics-in-go/
 
What's the fundamental difference between it and paraiso.marc's implementation? 

My view is that GenHello() is just a fancy way of paraiso.marc's getters. True?

Nick Patavalis

unread,
Nov 23, 2016, 10:14:36 AM11/23/16
to Tong Sun, golang-nuts

Yes... so every specific animal type implements it's own Output() method, which does the trivial IsA() part, and calls Animal's Output() for the common complicated parts...

Tong Sun

unread,
Nov 23, 2016, 10:17:11 AM11/23/16
to Nick Patavalis, golang-nuts
Can you make it work on play.golang.org, from this code https://play.golang.org/p/QjCtD9rGpa, according to your plan?

Tong Sun

unread,
Nov 23, 2016, 10:20:12 AM11/23/16
to Nick Patavalis, golang-nuts
Oh, I think you might not have notice this request from OP:

Please think of the "func Output()" as a very complicated function that I only want to define once at the base level, not to duplicate into each sub classes. 

Nick Patavalis

unread,
Nov 23, 2016, 12:09:03 PM11/23/16
to golang-nuts, nick.pa...@gmail.com

On Wednesday, November 23, 2016 at 5:17:11 PM UTC+2, Tong Sun wrote:
Can you make it work on play.golang.org, from this code https://play.golang.org/p/QjCtD9rGpa, according to your plan?

For this specific example, something like this: https://play.golang.org/p/FsorWRaLKk

/npat

chad...@gmail.com

unread,
Nov 23, 2016, 1:18:08 PM11/23/16
to golang-nuts, nick.pa...@gmail.com
Sorry, didn't read the whole thread, but here's how I tackled inheritance in my cross compiler: https://play.golang.org/p/UDp7nSLyl2. Granted, I am unsure about the unsafe thing, but the concept of a "method dispatcher" is likely what you need.

Tong Sun

unread,
Nov 23, 2016, 3:43:19 PM11/23/16
to chad...@gmail.com, golang-nuts
Thanks Chad. This is beyond my understanding for the moment, but I'm sure it'd be handy someday... Thanks.

Tong Sun

unread,
Nov 23, 2016, 3:50:00 PM11/23/16
to Nick Patavalis, golang-nuts

On Wed, Nov 23, 2016 at 12:09 PM, Nick Patavalis wrote:
For this specific example, something like this: https://play.golang.org/p/FsorWRaLKk

Thanks a lot Nick!

I've simplified it a bit. Now it is:

func (d Dog) Output() {
// Presumably complicated stuff, not re-implemented
d.Animal.Output(d.IsA())
}

I'm wondering if it is possible to somehow pass d.Speak as a function pointer to Animal.Output, so that inside Animal.Output, calling it will get to the correct dog.Speak or cat.Speak? Here is the code to start from:


Thanks

Tong Sun

unread,
Nov 23, 2016, 4:10:42 PM11/23/16
to golang-nuts, Nick Patavalis, Tahir Hashmi
I think I finally found the easiest way to make virtual function works, based on Nick Patavalis code and Tahir Hashmi's idea:


func (a Animal) Output(s Speaker) {
// Complicated stuff that must not be re-implemented
fmt.Print("I am a ", s.IsA(),
". My name is ", a.Name,
", aged ", a.Age,
", it is ", a.IsMale,
" I am male.\n ")
s.Speak()
}



Nick Patavalis

unread,
Nov 23, 2016, 4:27:41 PM11/23/16
to Tong Sun, golang-nuts, Tahir Hashmi

As I said
​ ​
before, Go's flavor of OO is different from that of other languages. Don't try to map concepts one-to-one, or you will end up with very contrived solutions. Always look at the big picture (why am I doing this?) and don't get side-tracked by synthetic examples.

Most important is defining the correct interfaces (how your objects should behave, what services they should provide); implementation reuse is secondary. You will always find more than one ways to avoid repeating the same code, on a per-case basis and you can always improve on that down the way, provided that your interfaces are correct.

Just my 2c
/npat

Tahir Hashmi

unread,
Nov 23, 2016, 7:57:48 PM11/23/16
to golang-nuts, codem...@gmail.com


On Wednesday, November 23, 2016 at 8:21:23 PM UTC+5:30, Tong Sun wrote:
 
I've tried to derive how to achieve a) implementation inheritance, followed by b) type substitution and c) enabling dynamic dispatch as in virtual functions in my blog post here: https://tech.t9i.in/2014/01/22/inheritance-semantics-in-go/
 
What's the fundamental difference between it and paraiso.marc's implementation? 

My view is that GenHello() is just a fancy way of paraiso.marc's getters. True?

The real difference is simulating the `this` pointer by defining the interface as a set of methods on itself. This allows modeling the interface purely on the desired common behaviour, whereas the getter approach requires including the getters in the interface out of necessity rather than need. It also precludes the possibility of adding more member fields in a "derived class" and using them in function overrides.

Haddock

unread,
Nov 24, 2016, 2:33:47 AM11/24/16
to golang-nuts


Am Dienstag, 22. November 2016 22:16:27 UTC+1 schrieb Tong Sun:
Hi, 

How to architect the OO's virtual function in Go?


Here is another blog that shows a way how to do this: http://objectscape.blogspot.de/2013/09/inner-pattern-to-mimic-method.html

Haddock

unread,
Nov 24, 2016, 3:00:03 AM11/24/16
to golang-nuts, nick.pa...@gmail.com, codem...@gmail.com

I think I finally found the easiest way to make virtual function works, based on Nick Patavalis code and Tahir Hashmi's idea:


func (a Animal) Output(s Speaker) {
// Complicated stuff that must not be re-implemented
fmt.Print("I am a ", s.IsA(),
". My name is ", a.Name,
", aged ", a.Age,
", it is ", a.IsMale,
" I am male.\n ")
s.Speak()
}




This is merely resorting to a shared function. This does not really cover overwriting in the OO sense. The article I mentioned shows how to do this: http://objectscape.blogspot.de/2013/09/inner-pattern-to-mimic-method.html

Cheers, Haddock
 

Nick Patavalis

unread,
Nov 24, 2016, 3:03:08 AM11/24/16
to Haddock, golang-nuts
Hi,

On Thu, Nov 24, 2016 at 9:33 AM, Haddock <ffm...@web.de> wrote:
>
> Here is another blog that shows a way how to do this:
> http://objectscape.blogspot.de/2013/09/inner-pattern-to-mimic-method.html
>

Clever! I would like, though, to underscore the conclusion reached by
the original post author:

My preliminary conclusion of all this is to use the "inner pattern"
in Go as an ex-post refactoring measure when code starts to lack
"too much" in transparency. To apply it to begin with is too much
pain where the gain in the end is uncertain. Otherwise, only apply
it ex-ante when it is clear from the beginning that the flexibility
will be needed anyway...

With this I agree fully.

/npat

Nick Patavalis

unread,
Nov 24, 2016, 3:14:37 AM11/24/16
to Haddock, golang-nuts, Tahir Hashmi
On Thu, Nov 24, 2016 at 10:00 AM, Haddock <ffm...@web.de> wrote:
>
> This is merely resorting to a shared function. This does not really
> cover overwriting in the OO sense. The article I mentioned shows how
> to do this:
> http://objectscape.blogspot.de/2013/09/inner-pattern-to-mimic-method.html

Not really! Tong Sun's Animal.Output() method (with a Speaker
argument) is pretty-much "isomorphic" with Plohmann's Mamal.ActInner()
method (with an Animal argument). Maybe it's not as clearly named, but
it is pretty-much the same approach.

/npat

xingtao zhao

unread,
Nov 24, 2016, 9:21:24 PM11/24/16
to golang-nuts, ffm...@web.de, codem...@gmail.com
Hi Tong,

Another implementation is https://play.golang.org/p/3_PDTCcJTi.
You could merge the interface Animal and ExtraFactsor together if this extra behavior is not needed. It is listed there just for demonstration.

xingtao zhao

unread,
Nov 24, 2016, 9:37:38 PM11/24/16
to golang-nuts, ffm...@web.de, codem...@gmail.com
Or this implementation: https://play.golang.org/p/5GqspHDJnF
But I prefer the previous version: https://play.golang.org/p/3_PDTCcJTi.

Tong Sun

unread,
Nov 24, 2016, 11:03:59 PM11/24/16
to xingtao zhao, golang-nuts
Oh, I like that. This is much better than my "double-passing" version, and so far the best. Thanks a lot!

--

Tong Sun

unread,
Nov 24, 2016, 11:29:41 PM11/24/16
to xingtao zhao, golang-nuts
Yeah, me too, preferring the first version, which I've archived as a masterpiece at 
with the following notes:

The best way to implement *virtual function* so far, that satisfies the
following challenges:

- Consider the "func Output()" as a *very complicated* function that I only
  want to define once at the base level, not to duplicate into each sub
  classes, yet it need to access member functions from sub classes.

- Meanwhile I have a *huge list* of common variables that I defined in my
  "base class", and using simple pure function will cause almost every
  single variable to be undefined: https://play.golang.org/p/QjCtD9rGpa
Reply all
Reply to author
Forward
0 new messages