Go polymorphism in function parameters - go

i found several questions with similar titles, but cannot find the answer to my question in them:
I have the following simple scenario:
types:
type intMappedSortable interface {
getIntMapping() int
}
type Rectangle struct {
length, width int
}
func (r Rectangle) getIntMapping() int {
return r.Area();
}
func (Rectangle r) getIntMapping() int {
return r.length * r.width;
}
main:
func main() {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
var values []int
values = make([]int, 0)
for i := 0; i < 10; i++ {
values = append(values, r.Intn(20))
}
var rects []Rectangle;
rects = make([]intMappedSortable, len(values));
for i,v:= range values {
r := Rectangle{v,v};
rects[i] = r;
}
for i,v:= range rects {
fmt.Println(v.Area());
}
rectsRet := make(chan intMappedSortable, len(rects));
sort(rects, rectsRet);
}
doWork:
func sort(values []intMappedSortable, out chan intMappedSortable) {...}
How do i manage to pass the Rectangles to the sorting function and then work with the sorted rectangles in main after it?
I tried:
var rects []*Rectangle;
rects = make([]*Rectangle, len(values));
as a habit from my C days, i don't want to copy the rectangles, just the addresses, so i can sort directly in the original slice, preventing 2 copy procedures for the whole data.
After this failed i tried:
var rects []intMappedSortable;
rects = make([]*Rectangle, len(values));
i learned that Go handles "polymorphism" by holding a pointer to the original data which is not exposed, so i changed *Rectangle to Rectangle, both gave me the compilererror that Rectangle is not []intMappedSortable
What obviously works is:
var rects []intMappedSortable;
rects = make([]intMappedSortable, len(values));
for i,v:= range values {
r := Rectangle{v,v};
rects[i] = r;
}
But are are these rectangles now copied or is just the memoryrepresentation of the interface with their reference copied? Additionally there now is no way to access length and width of the rectangles as the slice is not explicitly of type rectangle anymore.
So, how would i implement this scenario?
I want to create a slice of ANY structure, that implements the mapToInt(), sort the slice and then keep working with the concrete type after it
EDIT/FOLLOWUP:
I know its not good style, but i'm, experimenting:
can i somehow use type assertion with a dynamic type like:
func maptest(values []intMappedSortable, out interface{}) {
oType := reflect.TypeOf(out);
fmt.Println(oType); // --> chan main.intMappedSortable
test := values[0].(oType) //i know this is not working AND wrong even in thought because oType holds "chan intMappedSortable", but just for theory's sake
}
how could i do this, or is this not possible. I do not mean wether it is "meant to be done", i know it is not. But is it possible?^^

But are are these rectangles now copied or is just the memory representation of the interface with their reference copied?
The latter, see "what is the meaning of interface{} in golang?"
An interface value is constructed of two words of data:
one word is used to point to a method table for the value’s underlying type,
and the other word is used to point to the actual data being held by that value.
I want to create a slice of ANY structure, that implements the mapToInt(), sort the slice and then keep working with the concrete type after it
That isn't possible, as there is no genericity in Go.
See "What would generics in Go be?"
That is why you have projects like "gen":
generates code for your types, at development time, using the command line.
gen is not an import; the generated source becomes part of your project and takes no external dependencies.

Related

Should I encapsulate slice and maps in Go? If so, how to do it?

I want to create a structure which will be accessible in other packages, but I don't want to allow modify this structure. In other languages this is archived by making all fields private and exposing only public getters.
Solution with getters works fine for all data types except slices and maps because returned slices and maps aren't copied by default so they can be modified. The only solution that I managed to figure out is to create new map/slice and assign all items in a loop but this introduce a lot of repetitive and ugly code, especially for large nested structures.
package main
import (
"fmt"
)
type OtherStruct struct {
prop string
}
type Struct struct {
prop map[string]OtherStruct
}
func (s Struct) Prop() map[string]OtherStruct {
return s.prop
}
func (s Struct) Prop2() map[string]*OtherStruct {
prop := make(map[string]*OtherStruct, 0)
for k := range s.prop {
v := s.prop[k]
prop[k] = &v
}
return prop
}
func main() {
var s Struct;
// Simple getter
s = Struct{make(map[string]OtherStruct, 0)}
p1 := s.Prop()
fmt.Println(s) // &{map[]}
p1["something"] = OtherStruct{"test"}
fmt.Println(s) // {map[something:{test}]}
// Getter which copies map
s = Struct{make(map[string]OtherStruct, 0)}
p2 := s.Prop2()
fmt.Println(s) // &{map[]}
p2["something"] = &OtherStruct{"test"}
fmt.Println(s) // &{map[]}
}
Is there any better way to encapsulate slices/maps in Go? Or maybe I shouldn't use encapsulation at all in Go and use different approach?
Returning slice or map values is idiomatic Go. The user of your package will know how those data structures work in Go.
In your example, the user of Struct should know that adding a new entry into the returned map would reflect for any other user of the same map.
// Simple getter
s = Struct{make(map[string]OtherStruct, 0)}
p1 := s.Prop()
fmt.Println(s) // &{map[]}
p1["something"] = OtherStruct{"test"}
fmt.Println(s) // {map[something:{test}]}
You should worry about such things only in case of concurrency. That is, when multiple goroutines are accessing, and possibly changing, the elements inside your slice or map.

creating generic functions for multi type arrays in Go

I am trying to create a generic function that can handle actions on slices in Go... for instance, append an item of any type to a slice of that same type. This is simply a generic purpose for a more complex solution, but overall the issue boils down to this example:
package main
type car struct {
make string
color string
}
type submarine struct {
name string
length int
}
func genericAppender(thingList interface{}, thing interface{}) []interface{} {
return append(thingList, thing)
}
func main() {
cars := make([]car, 0, 10)
cars[0] = car{make: "ford", color: "red"}
cars[1] = car{make: "chevy", color: "blue"}
subs := make([]submarine, 0, 10)
subs[0] = submarine{name: "sally", length: 100}
subs[1] = submarine{name: "matilda", length: 200}
newCar := car{make: "bmw", color: "white"}
genericAppender(&cars, newCar)
}
The code playground is at this location
The above errors as follows:
prog.go:14: first argument to append must be slice; have interface {}
After this change you're still getting a runtime error (index out of range) however the problem is that thingList is not of type []interface{} but rather interface{} so you can't append to it. Here's an updated version of your code on playground that does a type assertion to convert it to an []interface{} in line with the append. In reality you need to do that on a separate line and check for errors.
https://play.golang.org/p/YMed0VDZrv
So to put some code here;
func genericAppender(thingList interface{}, thing interface{}) []interface{} {
return append(thingList.([]interface{}), thing)
}
will solve the basic problem you're facing. As noted, you still get runtime errors when indexing into the slice. Also, you could change the argument to avoid this by making it;
func genericAppender(thingList []interface{}, thing interface{}) []interface{} {
return append(thingList, thing)
}
Here's a complete example of the second type; https://play.golang.org/p/dIuW_UG7XY
Note I also corrected the runtime error. When you use make with 3 args they are, in this order, type, length, capacity. This means the length of the array is 0 so when you try to assign to indexes 0 and 1 it was causing a panic for IndexOutoFRange. Instead I removed the middle argument so it's make([]interface{}, 10) meaning the length is initially set to 10 so you can assign to those indexes.
In the answer above if you do the following then it throws error. This is what the original question was about:
//genericAppender(subs, newCar). // Throws "cannot use subs (type []submarine) as type []interface {} in argument to genericAppender"
The trick is to convert your slice of specific type into a generic []interface{}.
func convertToGeneric(thingList interface{}) []interface{} {
input := reflect.ValueOf(thingList)
length := input.Len()
out := make([]interface{},length)
for i:=0 ;i < length; i++ {
out[i] = input.Index(i).Interface()
}
return out
}
This you can call the function like this:
genericAppender(convertToGeneric(subs), newCar)
You can check modified working code here: https://play.golang.org/p/0_Zmme3c8lT
With Go 1.19 (Q4 2022), no need for interface, or "convert your slice of specific type into a generic []interface{}"
CL 363434 comes with a new slices packages:
// Package slices defines various functions useful with slices of any type.
// Unless otherwise specified, these functions all apply to the elements
// of a slice at index 0 <= i < len(s).
package slices
import "constraints"
// Grow increases the slice's capacity, if necessary, to guarantee space for
// another n elements. After Grow(n), at least n elements can be appended
// to the slice without another allocation. If n is negative or too large to
// allocate the memory, Grow panics.
func Grow[S ~[]T, T any](s S, n int) S {
return append(s, make(S, n)...)[:len(s)]
}
// Equal reports whether two slices are equal: the same length and all
// elements equal. If the lengths are different, Equal returns false.
// Otherwise, the elements are compared in index order, and the
// comparison stops at the first unequal pair.
// Floating point NaNs are not considered equal.
func Equal[T comparable](s1, s2 []T) bool {
if len(s1) != len(s2) {
return false
}
for i, v1 := range s1 {
v2 := s2[i]
if v1 != v2 {
return false
}
}
return true
}
// ...
Ian Lance Taylor confirms in issue 45955:
This package is now available at golang.org/x/exp/slices.
Per this thread, it will not be put into standard library until the 1.19 release.
We may of course adjust it based on anything we learn about having it in x/exp.

Why can't I access this field in an interface?

I am trying to understand interfaces better and am not understanding why s has no field Width. My example is here:
package main
import "fmt"
type shapes interface {
setWidth(float64)
}
type rect struct {
Width float64
}
func (r *rect) setWidth(w float64) {
r.Width = w
}
var allShapes = map[string]shapes{
"rect": &rect{},
}
func main() {
r := &rect{}
r.setWidth(5)
fmt.Println(r.Width) // this works
for _, s := range allShapes {
s.setWidth(7)
fmt.Println(s.Width) // why not???
}
}
Why does r have Width but s doesn't? The exact error I get is:
s.Width undefined (type shapes has no field or method Width)
shapes interface is what *rect implements, but it is not a concrete type *rect. It is, like any interface, a set of methods allowing any type satisfying it to pass, like giving a temporary visitor sticker to it to go up the building.
For instance, if there is a monkey (or for what it's worth, a dolphin) who can act and do everything a human can, in Go's building, he can pass the guard and go up the elevator. However, that doesn't make him genetically human.
Go is statically-typed, meaning even two types with the same underlying type cannot be dynamically converted or coerced to one another without a type assertion or consciously converting the type.
var a int
type myInt int
var b myInt
a = 2
b = 3
b = a // Error! cannot use a (type int) as type myInt in assignment.
b = myInt(a) // This is ok.
Imagine with me for a second this situation:
type MyInt int
type YourInt int
type EveryInt interface {
addableByInt(a int) bool
}
func (i MyInt) addableByInt(a int) bool {
// whatever logic doesn't matter
return true
}
func (i YourInt) addableByInt(a int) bool {
// whatever logic doesn't matter
return true
}
func main() {
// Two guys want to pass as an int
b := MyInt(7)
c := YourInt(2)
// Do everything an `EveryInt` requires
// and disguise as one
bi := EveryInt(b)
ci := EveryInt(c)
// Hey, look we're the same! That's the closest
// we can get to being an int!
bi = ci // This is ok, we are EveryInt brotherhood
fmt.Println(bi) // bi is now 2
// Now a real int comes along saying
// "Hey, you two look like one of us!"
var i int
i = bi // Oops! bi has been made
// ci runs away at this point
}
Now back to your scenerio--imagine a *circle comes along implementing shapes:
type circle struct {
Radius float64
}
func (c *circle) setWidth(w float64) {
c.Radius = w
}
*circle is totally passable as shapes but it does not have Width property because it is not a *rect. An interface cannot access an underlying type's property directly, but can only do so through implemented method set. In order to access a property, a type assertion is need on the interface so that instance becomes a concrete type:
var r *rect
// Verify `s` is in fact a `*rect` under the hood
if r, ok := s.(*rect); ok {
fmt.Println(r.Width)
}
This is at the core of why a statically-typed language like Go is always faster than dynamically-typed counterparts, which will almost always use some kind of reflection to handle type coercion dynamically for you.
s is a an implementer of the shapes interface, but in the for loop not typed as a rect.
If you do a type assertion to force s be of the concrete type like so:
s.(*rect).Width
You will get what you want.
You need to be careful about mixing concrete types and interfaces like that though.

Can we write a generic array/slice deduplication in go?

Is there a way to write a generic array/slice deduplication in go, for []int we can have something like (from http://rosettacode.org/wiki/Remove_duplicate_elements#Go ):
func uniq(list []int) []int {
unique_set := make(map[int] bool, len(list))
for _, x := range list {
unique_set[x] = true
}
result := make([]int, len(unique_set))
i := 0
for x := range unique_set {
result[i] = x
i++
}
return result
}
But is there a way to extend it to support any array? with a signature like:
func deduplicate(a []interface{}) []interface{}
I know that you can write that function with that signature, but then you can't actually use it on []int, you need to create a []interface{} put everything from the []int into it, pass it to the function then get it back and put it into a []interface{} and go through this new array and put everything in a new []int.
My question is, is there a better way to do this?
While VonC's answer probably does the closest to what you really want, the only real way to do it in native Go without gen is to define an interface
type IDList interface {
// Returns the id of the element at i
ID(i int) int
// Returns the element
// with the given id
GetByID(id int) interface{}
Len() int
// Adds the element to the list
Insert(interface{})
}
// Puts the deduplicated list in dst
func Deduplicate(dst, list IDList) {
intList := make([]int, list.Len())
for i := range intList {
intList[i] = list.ID(i)
}
uniques := uniq(intList)
for _,el := range uniques {
dst.Insert(list.GetByID(el))
}
}
Where uniq is the function from your OP.
This is just one possible example, and there are probably much better ones, but in general mapping each element to a unique "==able" ID and either constructing a new list or culling based on the deduplication of the IDs is probably the most intuitive way.
An alternate solution is to take in an []IDer where the IDer interface is just ID() int. However, that means that user code has to create the []IDer list and copy all the elements into that list, which is a bit ugly. It's cleaner for the user to wrap the list as an ID list rather than copy, but it's a similar amount of work either way.
The only way I have seen that implemented in Go is with the clipperhouse/gen project,
gen is an attempt to bring some generics-like functionality to Go, with some inspiration from C#’s Linq and JavaScript’s underscore libraries
See this test:
// Distinct returns a new Thing1s slice whose elements are unique. See: http://clipperhouse.github.io/gen/#Distinct
func (rcv Thing1s) Distinct() (result Thing1s) {
appended := make(map[Thing1]bool)
for _, v := range rcv {
if !appended[v] {
result = append(result, v)
appended[v] = true
}
}
return result
}
But, as explained in clipperhouse.github.io/gen/:
gen generates code for your types, at development time, using the command line.
gen is not an import; the generated source becomes part of your project and takes no external dependencies.
You could do something close to this via an interface. Define an interface, say "DeDupable" requiring a func, say, UniqId() []byte, which you could then use to do the removing of dups. and your uniq func would take a []DeDupable and work on it

Is there an easy way to iterate over a map in order?

This is a variant of the venerable "why is my map printing out of order" question.
I have a (fairly large) number of maps of the form map[MyKey]MyValue, where MyKey and MyValue are (usually) structs. I've got "less" functions for all the key types.
I need to iterate over the maps in order. (Specifically, the order defined by the less function on that type.) Right now, my code looks like this:
type PairKeyValue struct {
MyKey
MyValue
}
type PairKeyValueSlice []Pair
func (ps PairKeyValueSlice) Len() int {
return len(ps)
}
func (ps PairKeyValueSlice) Swap(i,j int) {
ps[i], ps[j] = ps[j], ps[i]
}
func (ps PairKeyValueSlice) Less(i,j int) {
return LessKey(ps[i].MyKey, ps[j].MyKey)
}
func NewPairKeyValueSlice(m map[MyKey]MyValue) (ps PairKeyValueSlice) {
ps = make(PairKeyValueSlice, len(m))
i := 0
for k,v := range m {
ps[i] = PairKeyValue{k,v}
i++
}
sort.Sort(ps)
}
And then, any time I want an in-order iteration, it looks like:
var m map[MyKey]MyValue
m = GetMapFromSomewhereUseful()
for _, kv := range NewPairKeyValueSlice(m) {
key := kv.MyKey
value := kv.MyValue
DoUsefulWork(key, value)
}
And this appears to largely work. The problem is that it is terribly verbose. Particularly since the problem at hand really has very little to do with implmenting ordered maps and is really about the useful work in the loop.
Also, I have several different keys and value types. So, every time I want to iterate over a map in order, I copy/paste all that code and do find/replace MyKey with the new key and MyValue with the new value. Copy/paste on that magnitude is... "smelly". It has already become a hassle, since I've already made a few errors that I had to fix several times.
This technique also has the downside that it requires making a full copy of all the keys and values. That is undesirable, but I don't see a way around it. (I could reduce it to just the keys, but it doesn't change the primary nature of the problem.)
This question is attempting the same thing with strings. This question does it with strings and ints. This question implies that you need to use reflection and will have to have a switch statement that switches on every possible type, including all user-defined types.
But with the people who are puzzled that maps don't iterate deterministically, it seems that there has got to be a better solution to this problem. I'm from an OO background, so I'm probably missing something fundamental.
So, is there a reasonable way to iterate over a map in order?
Update: Editing the question to have more information about the source, in case there's a better solution than this.
I have a lot of things I need to group for output. Each grouping level is in a structure that looks like these:
type ObjTypeTree struct {
Children map[Type]*ObjKindTree
TotalCount uint
}
type ObjKindTree struct {
Children map[Kind]*ObjAreaTree
TotalCount uint
}
type ObjAreaTree struct {
Children map[Area]*ObjAreaTree
TotalCount uint
Objs []*Obj
}
Then, I'd iterate over the children in the ObjTypeTree to print the Type groupings. For each of those, I iterate over the ObjKindTree to print the Kind groupings. The iterations are done with methods on the types, and each kind of type needs a little different way of printing its grouping level. Groups need to be printed in order, which causes the problem.
Don't use a map if key collating is required. Use a B-tree or any other/similar ordered container.
I second jnml's answer. But if you want something shorter than you have and are willing to give up compile time type safety, then my library might work for you. (It's built on top of reflect.) Here's a full working example:
package main
import (
"fmt"
"github.com/BurntSushi/ty/fun"
)
type OrderedKey struct {
L1 rune
L2 rune
}
func (k1 OrderedKey) Less(k2 OrderedKey) bool {
return k1.L1 < k2.L1 || (k1.L1 == k2.L1 && k1.L2 < k2.L2)
}
func main() {
m := map[OrderedKey]string{
OrderedKey{'b', 'a'}: "second",
OrderedKey{'x', 'y'}: "fourth",
OrderedKey{'x', 'x'}: "third",
OrderedKey{'a', 'b'}: "first",
OrderedKey{'x', 'z'}: "fifth",
}
for k, v := range m {
fmt.Printf("(%c, %c): %s\n", k.L1, k.L2, v)
}
fmt.Println("-----------------------------")
keys := fun.QuickSort(OrderedKey.Less, fun.Keys(m)).([]OrderedKey)
for _, k := range keys {
v := m[k]
fmt.Printf("(%c, %c): %s\n", k.L1, k.L2, v)
}
}
Note that such a method will be slower, so if you need performance, this is not a good choice.

Resources