How to realize "Virtual Inheritance" in Go?
go lang compiler version: "1.3.1 windows/amd64", does it support "Virtual Inheritance" for Go?
I never heard a C like language could support "Virtual", so I really misunderstand.
Virtual Inheritance solves a problem which does not exist if you don't have multiple inheritance. Consider the following inheritance tree:
A
/ \
B C
\ /
D
If classes B and C both provide a data-member (or method for that matter) with the same name then when accessing said member in D, you need a way to disambiguate whose ancestor's data-member (or method) you wish to access.
Virtual Inheritance is C++'s solution.
In Go you don't have inheritance to begin with; only composition and you can embed at most 1 member of any given type at once.
http://play.golang.org/p/1iYzdoFqIC
package main
type B struct {
}
func (b B) Foo() {}
type C struct {
}
func (c C) Foo() {}
type D struct {
B
C
}
func main() {
d := D{B{}, C{}}
// d.Foo() // <- ambiguous
d.B.Foo() // <- ok
d.C.Foo() // <- ok
}
Virtual Inheritance not only solves the problem of multiple inheritance but also gives us polymorphism.
package main
import (
"fmt"
"math"
)
type Form interface {
Color() string
Area() float64
}
type form struct {
color string
}
func (f *form) Color() string {
return f.color
}
type Circle struct {
form
radius float64
}
func (k *Circle) Area() float64 {
return math.Pi * k.radius * k.radius
}
type Rectangle struct {
form
width, height float64
}
func (r *Rectangle) Area() float64 {
return r.width * r.height
}
func main() {
var forms [2]Form
forms[0] = &Circle{
form: form{ "black" },
radius: 5.0,
}
forms[1] = &Rectangle{
form: form{ "read" },
width: 2.0,
height: 3.0,
}
for _, f := range forms {
fmt.Printf("%s: %.2f\n", f.Color(), f.Area())
}
}
Here we have an array of things that have something in common (Color and Area) and we can just iterate over this array calling the same functions and always the right thing will happen.
This is just one advantage of polymorphism. It plays a big role in most design patterns.
"Virtual Inheritance" is something more like this
http://play.golang.org/p/8RvPmB3Pof
package main
type A struct {
virtual int
}
func (a *A) set(v int) {
a.virtual = v
}
func (a *A) get() int {
return a.virtual
}
type B struct {
*A
}
type C struct {
*A
}
type D struct {
*B
*C
}
func main() {
a := &A{}
b := &B{a}
c := &C{a}
d := &D{b, c}
d.B.set(3)
println(d.C.get())
return
}
Related
I have a couple custom types that I need to process in the same way. It seems like a perfect use for generics. In the process, I need to call methods on instances of the types, and those methods return different instances of the same types, and then I need to call methods on those returned instances, which I can't get to work. For the purpose of this question, I've fabricated a much simpler set of types and a process that exemplifies the problem I'm running in to.
Here's a working example without generics that shows the types (Circle and Square), and a process (.Bigger().Smaller()) I'll be trying to abstract into a generic function later (online demo):
package main
import (
"fmt"
)
type Circle struct{ r float64 }
func NewCircle(r float64) *Circle { return &Circle{r: r} }
func (c *Circle) Radius() float64 { return c.r }
func (c *Circle) Bigger() *Circle { return &Circle{r: c.r + 1} }
func (c *Circle) Smaller() *Circle { return &Circle{r: c.r - 1} }
type Square struct{ s float64 }
func NewSquare(s float64) *Square { return &Square{s: s} }
func (s *Square) Side() float64 { return s.s }
func (s1 *Square) Bigger() *Square { return &Square{s: s1.s + 1} }
func (s1 *Square) Smaller() *Square { return &Square{s: s1.s - 1} }
func main() {
fmt.Println(NewCircle(3).Bigger().Smaller().Radius()) // prints 3
fmt.Println(NewSquare(6).Bigger().Smaller().Side()) // prints 6
}
The first thing I do to make a generic function is to define a type constraint:
type ShapeType interface {
*Circle | *Square
}
I'll be passing a ShapeType to a process method, and I need to be able to call methods on the ShapeType instance, so I need to define another type constraint which specifies the methods that can be called on a ShapeType:
type Shape[ST ShapeType] interface {
Bigger() ST
Smaller() ST
}
With these, I can write a process method (online demo):
func process[ST ShapeType](s Shape[ST]) ST {
return s.Bigger().Smaller()
}
This fails to compile however, as the return value of s.Bigger() is an ST, not a Shape[ST], so go doesn't know that it can then call Smaller() on the return value of s.Bigger(). In go's words:
s.Bigger().Smaller undefined (type ST has no field or method Smaller)
If Bigger() and Smaller() didn't return instances of their receiver types, I could write:
type Shape interface {
*Circle | *Square
Bigger()
Smaller()
}
func process[S Shape](x S) S {
x.Bigger().Smaller()
return x // I guess we wouldn't even have to return x, but just for example's sake
}
Instead I would need to write:
type Shape interface {
*Circle | *Square
Bigger() Shape
Smaller() Shape
}
and it appears go doesn't like self-referential type constraints.
If it were possible to assert/convert a concrete type to an interface it conforms to, then I could make it work, but it doesn't appear to be possible to do that (online demo):
func process[ST ShapeType](s Shape[ST]) ST {
s1 := s.Bigger()
s2 := s1.(Shape[ST]) // go is not happy here
return s2.Smaller()
}
For this, go says:
cannot use type assertion on type parameter value s1 (variable of type ST constrained by ShapeType)
I don't know what else to try.
Is it possible to work with these kinds of types with generics? If so, how?
Combine your two attempted interfaces together:
type Shape[ST any] interface {
*Circle | *Square
Bigger() ST
Smaller() ST
}
And then instantiate the constraint of process with the type parameter itself:
func process[ST Shape[ST]](s ST) ST {
return s.Bigger().Smaller()
}
Adding the union element *Circle | *Square into Shape[ST any] means that only those two types will be able to implement the interface
Then using the type parameter in the method signature, like Bigger() ST, means that whichever type is passed has a method that returns itself.
If you want to keep ShapeType as a separated interface, you can write Shape as:
type Shape[ST any] interface {
ShapeType
Bigger() ST
Smaller() ST
}
You can also use process method with type inference, without any issue:
func main() {
c1 := NewCircle(3)
c2 := process(c1)
fmt.Println(c2.Radius()) // prints 3 as expected
fmt.Printf("%T\n", c2) // *main.Circle
s1 := NewSquare(6)
s2 := process(s1)
fmt.Println(s2.Side()) // prints 6 as expected
fmt.Printf("%T\n", s2) // *main.Square
}
Final playground: https://go.dev/play/p/_mR4wkxXupH
Could someone please explain (or if it's a long thing to explain at least point me to some documentation) why this code is behaving like it does. It's a bit lengthy but I couldn't figure out a good way of shortening it without losing too much info.
What confuses me is that I need to have the method "Find" on each type of struct for it to be recognized as the type it is. With Find on only the baseTypeImp is it printing that each is baseTypeImp, if I have it on baseTypeImp and advancedBaseTypeImp are those two identified correctly, but not the last type.
PlaygroundLink
My Code
package main
import (
"fmt"
"reflect"
)
type BaseType interface {
Name() string
Find(string) BaseType
Children() []BaseType
Options()
}
type baseTypeImp struct {
name string
children []BaseType
}
func (b baseTypeImp) Options() {
fmt.Println("Not implemented")
}
func (b baseTypeImp) Find(name string) BaseType {
if b.name == name {
return b
}
for _, c := range b.children {
if m := c.Find(name); m != nil {
return m
}
}
return nil
}
func (b baseTypeImp) Name() string {
return b.name
}
func (b baseTypeImp) Children() []BaseType {
return b.children
}
type AdvancedBaseType interface {
Value()
}
type advancedBaseTypeImp struct {
baseTypeImp
}
func (a advancedBaseTypeImp) Options() {
fmt.Println("Is implemented")
}
func (a advancedBaseTypeImp) Value() {
fmt.Println("Value called")
}
func (a advancedBaseTypeImp) Find(name string) BaseType {
if a.name == name {
return a
}
for _, c := range a.children {
if m := c.Find(name); m != nil {
return m
}
}
return nil
}
type reallyAdvancedBaseTypeImp advancedBaseTypeImp
func newThingy(name, variant string, children []BaseType) BaseType {
base := baseTypeImp{name: name, children: children}
switch variant {
case "advanced":
return advancedBaseTypeImp{baseTypeImp: base}
case "reallyAdvanced":
return reallyAdvancedBaseTypeImp{baseTypeImp: base}
}
return base
}
func whatType(b BaseType) {
if b == nil {
return
}
fooType := reflect.TypeOf(b)
fmt.Println(b.Name(), " is type ", fooType.Name())
b.Options()
}
func main() {
advanced := make([]BaseType, 0, 5)
for i := 0; i < 5; i++ {
advanced = append(advanced, newThingy(fmt.Sprintf("Advanced %d", i), "advanced", nil))
}
reallyAdvanced := make([]BaseType, 0, 2)
for i := 0; i < 2; i++ {
reallyAdvanced = append(reallyAdvanced, newThingy(fmt.Sprintf("ReallyAdvanced %d", i), "reallyAdvanced", advanced[i:i+3]))
}
basic := newThingy("Basic parent", "basic", reallyAdvanced)
whatType(basic.Find("Basic parent"))
for i := 0; i < 2; i++ {
whatType(basic.Find(fmt.Sprintf("Advanced %d", i)))
whatType(basic.Find(fmt.Sprintf("ReallyAdvanced %d", i)))
}
}
The output now is:
Basic parent is type baseTypeImp
Not implemented
Advanced 0 is type advancedBaseTypeImp
Is implemented
ReallyAdvanced 0 is type baseTypeImp
Not implemented
Advanced 1 is type advancedBaseTypeImp
Is implemented
ReallyAdvanced 1 is type baseTypeImp
Not implemented
What I'd like to have is:
Basic parent is type baseTypeImp
Not implemented
Advanced 0 is type advancedBaseTypeImp
Is implemented
ReallyAdvanced 0 is type reallyAdvancedBaseTypeImp
Is implemented
Advanced 1 is type advancedBaseTypeImp
Is implemented
ReallyAdvanced 1 is type reallyAdvancedBaseTypeImp
Is implemented
Is there a way to get this working without having to implement Find on each level? It kind of defeats the purpose of having the common methods in the top layer struct. And of course, if possible explain why it's behaving the way it does.
When you embed a struct into another one, the outer struct gets the methods of the embedded one.
type inner struct {
}
func (i inner) f() {}
type outer struct {
inner
}
...
x:=outer{}
x.f()
You can think of this as syntactic sugar for:
type outer2 struct {
i inner
}
y.i.f()
When you embed inner without a field name, you simply drop the i in the call. You can still call x.inner.f() in the first example.
When you redefine a type, it does not get the methods defined for its base type.
type newtype inner
newtype does not have f() defined. However, if inner also embeds other structs (like you do), then those are still embedded in the new type, so those function are defined:
type deepest struct {}
func (deepest) deep() {}
type inner struct{}
func (inner) in() {}
type outer inner
Above, the type outer does not have the function in, but it has deep.
So when you call reallyAdvancedBaseTypeImp.Find(), what you're really doing is reallyAdvancedBaseTypeImp.baseTypeImp.Find(), which works on that portion of the struct. That's why you're getting the types you get.
You're trying to emulate method overriding. You can't really do that. Formulate your problem in a different way.
I have two types, B and C, which share all methods, but implement one of them differently. I would like to express this by having a parent type A, containing the implementations of the shared methods, and embedding A in B and C. (Don't Repeat Yourself!) The snag is that the method that differs between B and C is called in many of the shared methods. What's the idiomatic way to structure such code?
My current implementation looks essentially like this (https://play.golang.org/p/RAvH_hBFDN; based on an example by Dave Cheney):
package main
import (
"fmt"
)
type Legger interface {
Legs() int
}
type Cat struct {
Name string
L Legger
}
// Cat has many methods like this, involving calling c.L.Legs()
func (c Cat) PrintLegs() {
fmt.Printf("I have %d legs.\n", c.L.Legs())
}
// OctoCat is a Cat with a particular implementation of Legs
type OctoCat struct {
Cat
}
func (c OctoCat) Legs() int {
return 8
}
// TetraCat has a different implementation of Legs
type TetraCat struct {
Cat
}
func (c TetraCat) Legs() int {
return 4
}
func main() {
c := TetraCat{Cat{"Steve",nil}}
c.L = &c
c.PrintLegs() // want 4
o := OctoCat{Cat{"Bob",nil}}
o.L = &o
o.PrintLegs() // want 8
}
The type definitions themselves look nice and clean, but the initialization code in main is wacky (first the nil in the struct literal, then c.L = &c, what?). Is there a better solution?
A similar pattern was presented in is it possible to call overridden method from parent struct in golang?, but the question of whether this is the idiomatic way to proceed was not answered.
Two approaches I would consider for solving this:
1. Refactor your code to have a single type Cat with the fields Name string and Legs int:
package main
import (
"fmt"
)
type Cat struct {
Name string
Legs int
}
func (c *Cat) PrintLegs() {
fmt.Printf("I have %d legs.\n", c.Legs)
}
func main() {
c := &Cat{"Steve", 4}
c.PrintLegs() // want 4
o := &Cat{"Bob", 8}
o.PrintLegs() // want 8
}
https://play.golang.org/p/_PNAu3sgG8
2. Do away with the Cat type and just have TetraCat and OctoCat implement the Legger interface:
package main
import (
"fmt"
)
type Legger interface {
Legs() int
}
func PrintLegs(l Legger) {
fmt.Printf("I have %d legs.\n", l.Legs())
}
// OctoCat is a Cat with a particular implementation of Legs
type OctoCat struct {
Name string
}
func (c *OctoCat) Legs() int {
return 8
}
// TetraCat has a different implementation of Legs
type TetraCat struct {
Name string
}
func (c *TetraCat) Legs() int {
return 4
}
func main() {
c := &TetraCat{"Steve"}
PrintLegs(c) // want 4
o := &OctoCat{"Bob"}
PrintLegs(o) // want 8
}
https://play.golang.org/p/m4QvKhSj0w
My use case is, I need to have several structs in Go, who will have methods of same signatures and not necessarily have to have all the methods. The following code describes the requirements and also represents my current solution.
type calc struct {
fn func(a, b int) int
gn func(a string) bool
name string
}
func add(a, b int) int {
return a + b
}
func bar(foo string) bool {
// do something
}
func sub(a, b int) int {
return a - b
}
func main() {
for c := range []calc{{
fn: add,
gn: bar,
name: "addition",
}, {
fn: sub,
name: "subtraction",
}} {
fmt.Printf("%s(10, 15) returned: %d\n", c.name, c.fn(10, 15))
if c.gn != nil {
c.gn(c.name)
}
}
}
My question is how to improve this code? What's the best way to achieve this in Go? Can I achieve a better solution using interface?
Use interfaces.
type Op interface {
Name() string
Do(a, b int) int
}
type Add struct{}
func (Add) Name() string { return "add" }
func (Add) Do(a, b int) int { return a + b }
type Sub struct{}
func (Sub) Name() string { return "sub" }
func (Sub) Do(a, b int) int { return a - b }
Playground: http://play.golang.org/p/LjJt6D0hNF.
EDIT: Since you've edited your question, here is an example of how you could use asserting and interface to a broader interface for your task:
type RevOp interface {
Op
ReverseDo(a, b int) int
}
// ...
func (Add) ReverseDo(a, b int) int { return a - b }
// ...
fmt.Printf("%s(10, 15) returned: %d\n", op.Name(), op.Do(10, 15))
if op, ok := op.(RevOp); ok {
fmt.Printf("reverse of %s(10, 15) returned: %d\n", op.Name(), op.ReverseDo(10, 15))
}
Playground: http://play.golang.org/p/MQ6LlPDcEi.
I've discussed with some people in person and apparently my solution was correct. While it can be rewritten using interface, that's not necessarily an improvement.
I have problems understanding methods and receivers in Go. Let's say we have this code:
package main
import ("fmt"; "math")
type Circle struct {
x, y, r float64
}
func (c *Circle) area() float64 {
return math.Pi * c.r * c.r
}
func main() {
c := Circle{0, 0, 5}
fmt.Println(c.area())
}
(c *Circle) in the definition of the area function is said to be a receiver and in the main we can call area and pass c by reference without the use of pointers. I can edit the code to the following and it works the same way:
package main
import ("fmt"; "math")
type Circle struct {
x, y, r float64
}
func circleArea(c *Circle) float64 {
return math.Pi * c.r*c.r
}
func main() {
c := Circle{0, 0, 5}
fmt.Println(circleArea(&c))
}
Now is this just a syntactical difference between the two snippets of code or is there something structurally different going on on a deeper level?
The difference isn't just syntax. With a method, your circle type could fulfill an interface, but the function doesn't let you do that:
type areaer interface {
area() float64
}