Prototyping struct in Go - go

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.

Related

How to transfer interface to specified type by using generic

There is an Interface declare and many structs that implement it
type DataInterface interface {
Get(string) string
}
type DataA struct {
d map[string]string
}
func (d *DataA) Get(key string) string {
return d.d[key]
}
func (d *DataA) GetId() string {
return d.Get("id")
}
type DataB struct {
d map[string]string
}
func (d *DataB) Get(key string) string {
return d.d[key]
}
func (d *DataB) GetFile() string {
return d.Get("file")
}
type DataC...
Also includes DataC,D,E...
and I will store these DataX structs instance into a type DataSlice []DataInterface
Now, If I want to get DataX , I can do this:
type DataSlice []DataInterface
func (d DataSlice) GetA() []*DataA {
var ret []*DataA
for _, di := range d {
if v, ok := di.(*DataA); ok {
ret = append(ret, v)
}
}
return ret
}
func (d DataSlice) GetB() []*DataB {
var ret []*DataB
for _, di := range d {
if v, ok := di.(*DataB); ok {
ret = append(ret, v)
}
}
return ret
}
func (d DataSlice) GetC() .....
Obviously there's a lot of repetitive code here:
var ret []*DataX
for _, di := range d {
if v, ok := di.(*DataX); ok {
ret = append(ret, v)
}
}
So I think about that I can use generic to slove this, then I define this function:
func GetDataX[T any] (d DataInterface) *T {
return d.(*T)
}
but got error: Impossible type assertion: '*T' does not implement 'DataInterface
So, I want to know is this way really impossible? Or it could be completed by the other way?
You should be able to handle your needs with the following code:
package main
import "fmt"
// interface
type DataInterface interface {
Get(string) string
}
// struct implementing the interface
type DataA struct {
d map[string]string
}
func (d DataA) Get(key string) string {
return d.d[key]
}
type DataB struct {
d map[string]string
}
func (d DataB) Get(key string) string {
return d.d[key]
}
type DataSlice []DataInterface
func GetDataX[T any](d DataInterface) T {
return d.(T)
}
func main() {
a := DataA{map[string]string{"a": "1"}}
b := DataB{map[string]string{"b": "2"}}
ds := DataSlice{a, b}
for _, v := range ds {
if value, ok := v.(DataA); ok {
fmt.Printf("A\t%q\n", GetDataX[DataA](value))
continue
}
if value, ok := v.(DataB); ok {
fmt.Printf("B\t%q\n", GetDataX[DataB](value))
continue
}
// add unknown type handling logic here
}
}
First, I simplified the code to take into consideration only the DataA and DataB structs. Then, I changed the pointer receivers to value receivers as you're not going to change the state of the actual instance passed to the methods. Thanks to this change the GetDataX works successfully and you're able to get the info for all of your similar structs.
Let me know if this solves your issues or if you need something else, thanks!

Simplify casting in Go when taking any interface as a parameter

I have a struct like so,
//
// HandlerInfo is used by features in order to register a gateway handler
type HandlerInfo struct {
Fn func(interface{})
FnName string
FnRm func()
}
where I want to pass a func:
func StarboardReactionHandler(e *gateway.MessageReactionAddEvent) {
// foo
}
i := HandlerInfo{Fn: StarboardReactionHandler}
Unfortunately, this results in:
Cannot use 'StarboardReactionHandler' (type func(e *gateway.MessageReactionAddEvent)) as the type func(interface{})
I found this workaround:
func StarboardReactionHandler(e *gateway.MessageReactionAddEvent) {
// foo
}
func handlerCast(e interface{}) {
StarboardReactionHandler(e.(*gateway.MessageReactionAddEvent))
}
i := HandlerInfo{Fn: handlerCast}
Is there some way that I can simplify needing handlerCast, such as doing it inside my StarboardReactionHandler or in HandlerInfo? Maybe with generics or reflection? I basically just want to minimize the syntax / boilerplate that's required here.
you can use interface{}.(type)
follow is a exmple:
package main
import "fmt"
type HandlerInfo struct {
Fn func(interface{})
FnName string
FnRm func()
}
type MessageReactionAddEvent = func(a, b int) int
func StarboardReactionHandler(e interface{}) {
switch e.(type) {
case MessageReactionAddEvent:
fmt.Printf("%v\n", (e.(MessageReactionAddEvent))(1, 2))
}
}
func add(a, b int) int {
return a + b
}
func main() {
i := HandlerInfo{Fn: StarboardReactionHandler}
i.Fn(add)
}

How to implement a generic Either type in Go?

With the new generics in Go 1.18, I thought it might be possible to create a 'Either[A,B]' type that can be used to express that something could be either of type A or type B.
A situation where you might use this is in situations where a function might return one of two possible values as a result (e.g. one for 'normal' result and one for an error).
I know the 'idiomatic' Go for errors would be to return both a 'normal' value and an error value, returning a nil for either the error or the value. But... it sort of bothers me that we are essentially saying 'this returns A and B' in the type, where what we really mean to say is 'this returns A or B'.
So I thought maybe we can do better here, and I thought this might also be a good exercise to see/test the boundaries of what we can do with these new generics.
Sadly,try as I might, so far I have not been able solve the exercise and get anything working/compiling. From one of my failed attempts, here is an interface I'd like to implement somehow:
//A value of type `Either[A,B]` holds one value which can be either of type A or type B.
type Either[A any, B any] interface {
// Call either one of two functions depending on whether the value is an A or B
// and return the result.
Switch[R any]( // <=== ERROR: interface methods must have no type parameters
onA func(a A) R),
onB func(b B) R),
) R
}
Unfortunately, this fails rather quickly because declaring this interface isn't allowed by Go. Apparantly because 'interface methods must have no type parameters'.
How do we work around this restriction? Or is there simply no way to create a 'type' in Go that accurately expresses the idea that 'this thing is/returns either A or B' (as opposed to a tuple of both A and B).
If I had to do this, I would look up a functional programming language(like OCaml) and knock-off their solution of the either type..
package main
import (
"errors"
"fmt"
"os"
)
type Optional[T any] interface {
get() (T, error)
}
type None[T any] struct {
}
func (None[T]) get() (T, error) {
var data T
return data, errors.New("No data present in None")
}
type Some[T any] struct {
data T
}
func (s Some[T]) get() (T, error) {
return s.data, nil
}
func CreateNone[T any]() Optional[T] {
return None[T]{}
}
func CreateSome[T any](data T) Optional[T] {
return Some[T]{data}
}
type Either[A, B any] interface {
is_left() bool
is_right() bool
find_left() Optional[A]
find_right() Optional[B]
}
type Left[A, B any] struct {
data A
}
func (l Left[A, B]) is_left() bool {
return true
}
func (l Left[A, B]) is_right() bool {
return false
}
func left[A, B any](data A) Either[A, B] {
return Left[A, B]{data}
}
func (l Left[A, B]) find_left() Optional[A] {
return CreateSome(l.data)
}
func (l Left[A, B]) find_right() Optional[B] {
return CreateNone[B]()
}
type Right[A, B any] struct {
data B
}
func (r Right[A, B]) is_left() bool {
return false
}
func (r Right[A, B]) is_right() bool {
return true
}
func right[A, B any](data B) Either[A, B] {
return Right[A, B]{data}
}
func (r Right[A, B]) find_left() Optional[A] {
return CreateNone[A]()
}
func (r Right[A, B]) find_right() Optional[B] {
return CreateSome(r.data)
}
func main() {
var e1 Either[int, string] = left[int, string](4143)
var e2 Either[int, string] = right[int, string]("G4143")
fmt.Println(e1)
fmt.Println(e2)
if e1.is_left() {
if l, err := e1.find_left().get(); err == nil {
fmt.Printf("The int is: %d\n", l)
} else {
fmt.Fprintln(os.Stderr, err)
}
}
if e2.is_right() {
if r, err := e2.find_right().get(); err == nil {
fmt.Printf("The string is: %s\n", r)
} else {
fmt.Fprintln(os.Stderr, err)
}
}
}
The Either could be modeled as a struct type with one unexported field of type any/interface{}. The type parameters would be used to ensure some degree of compile-time type safety:
type Either[A, B any] struct {
value any
}
func (e *Either[A,B]) SetA(a A) {
e.value = a
}
func (e *Either[A,B]) SetB(b B) {
e.value = b
}
func (e *Either[A,B]) IsA() bool {
_, ok := e.value.(A)
return ok
}
func (e *Either[A,B]) IsB() bool {
_, ok := e.value.(B)
return ok
}
If Switch has to be declared as a method, it can't be parametrized in R by itself. The additional type parameter must be declared on the type definition, however this might make usage a bit cumbersome because then R must be chosen upon instantiation.
A standalone function seems better — in the same package, to access the unexported field:
func Switch[A,B,R any](e *Either[A,B], onA func(A) R, onB func(B) R) R {
switch v := e.value.(type) {
case A:
return onA(v)
case B:
return onB(v)
}
}
A playground with some code and usage: https://go.dev/play/p/g-NmE4KZVq2
You can use the https://github.com/samber/mo library (disclaimer: I'm the project author).
Either signature is:
type Either[L any, R any] struct {}
Some examples:
import "github.com/samber/mo"
left := lo.Left[string, int]("hello")
left.LeftOrElse("world")
// hello
left.RightOrElse(1234)
// 1234
left.IsLeft()
// true
left.IsRight()
// false
Your question about a Switch pattern can be implemented this way:
import "github.com/samber/mo"
left := lo.Left[string, int]("hello")
result := left.Match(
func(s string) Either[string, int] {
// <-- should enter here
return lo.Right[string, int](1234)
},
func(i int) Either[string, int] {
// <-- should not enter here
return lo.Right[string, int](i * 42)
},
)
result.LeftOrElse("world")
// world
result.RightOrElse(42)
// 1234
A solution finally came to me. The key was defining the 'Either' type as a 'struct' instead of an interface.
type Either[A any, B any] struct {
isA bool
a A
b B
}
func Switch[A any, B any, R any](either Either[A, B],
onA func(a A) R,
onB func(b B) R,
) R {
if either.isA {
return onA(either.a)
} else {
return onB(either.b)
}
}
func MakeA[A any, B any](a A) Either[A, B] {
var result Either[A, B]
result.isA = true
result.a = a
return result
}
func MakeB[A any, B any](b B) Either[A, B] {
... similar to MakeA...
}
That works, but at the 'price' of really still using a 'tuple-like' implementation under the hood were we store both an A and a B but ensure it is only possible to use one of them via the public API.
I suspect this is the best we can do given the restrictions Go puts on us.
If someone has a 'workaround' that doesn't essentially use 'tuples' to represent 'unions'. I would consider that a better answer.

Reflect on list of interfaces

I have read several examples/questions about reflection in Go, but I'm still unable to understand what I'm supposed to do with my list of interfaces.
Below is a stripped down version of the real use case.
I have several types complying with a given interface:
type Foo interface {
Value() int
}
type bar struct {
value int
}
func (b bar) Value() int {
return b.value
}
type baz struct{}
func (b baz) Value() int {
return 42
}
I have a list of such guys
type Foos []Foo
var foos = Foos{
bar{},
baz{},
}
and I would like to traverse this list by changing the value of the members that have a value field.
for k := range foos {
change(&foos[k])
}
But I'm unable to find the right path
func change(g *Foo) {
t := reflect.TypeOf(g).Elem()
fmt.Printf("t: Kind %v, %#v\n", t.Kind(), t)
v := reflect.ValueOf(g).Elem()
fmt.Printf("v: Kind %v, %#v, %v\n", v.Kind(), v, v.CanAddr())
if f, ok := t.FieldByName("value"); ok {
fmt.Printf("f: %#v, OK: %v\n", f, ok)
vf := v.FieldByName("value")
fmt.Printf("vf: %#v: %v\n", vf, vf.CanAddr())
vf.SetInt(51)
}
}
As you can see, I'm not sure how to glue together the TypeOf and ValueOf bits...
The full example is on Go Playground.
There are a couple of issues here. First, it's not possible to set an unexported field. Here's a change to make the field exported:
type Foo interface {
// Rename Value to GetValue to avoid clash with Value field in bar
GetValue() int
}
type bar struct {
// export the field by capitalizing the name
Value int
}
func (b bar) GetValue() int {
return b.Value
}
type baz struct{}
func (b baz) GetValue() int {
return 42
}
The next issue is a bar interface value is not addressable. To fix this, use a *bar in the slice instead instead of a bar:
func (b *bar) GetValue() int {
return b.Value
}
...
var foos = Foos{
&bar{},
baz{},
}
With these changes, we can write the function to set the value:
func change(g Foo) {
v := reflect.ValueOf(g)
// Return if not a pointer to a struct.
if v.Kind() != reflect.Ptr {
return
}
v = v.Elem()
if v.Kind() != reflect.Struct {
return
}
// Set the field Value if found.
v = v.FieldByName("Value")
if !v.IsValid() {
return
}
v.SetInt(31)
}
Run it on the playground
The above answers the question, but it may not be the best solution to the actual problem. A better solution may be to define a setter interface:
type ValueSetter interface {
SetValue(i int)
}
func (b *bar) Value() int {
return b.value
}
func (b *bar) SetValue(i int) {
b.value = i
}
func change(g Foo) {
if vs, ok := g.(ValueSetter); ok {
vs.SetValue(31)
}
}
Run it on the playground
Please don't use your current approach. It has several drawbacks.
It's confusing (as you've discovered)
It's slow. Reflection is always slow.
It's inflexible. Your loop, not the type itself, must understand the capabilities of each type implementation.
Instead, just expand your interface to include a setter method--possibly an optional setter method. Then you can loop through the items and set the value for those that support it. Example:
// FooSetter is an optional interface which allows a Foo to set its value.
type FooSetter interface {
SetValue(int)
}
// SetValue implements the optional FooSetter interface for type bar.
func (b *bar) SetValue(v int) {
b.value = v
}
Then make your loop look like this:
for _, v := range foos {
if setter, ok := v.(FooSetter); ok {
setter.SetValue(51)
}
}
Now when you (or a third party user of your library) adds a type Baz that satisfies the FooSetter interface, you don't have to modify your loop at all.

Generic method to iterate over collection (slice) of structs

I have following code in Go:
type Foo struct { Id int }
type Bar struct { Id int }
func getIdsFoo(foos []Foo) {
ids = make([]int, len(foos))
// iterate and get all ids to ids array
}
func getIdsBar(bars []Bar) {
ids = make([]int, len(bars))
// iterate and get all ids to ids array
}
Is there a clever way to create a function getIds([]Idable) that can take any struct that have method GetId() implemented?
type Identifiable interface {
GetId() int
}
func GatherIds(ys []Identifiable) []int {
xs := make([]int, 0, len(ys))
for _, i := range ys {
xs = append(xs, i.GetId())
}
return xs
}
sort uses a design patter that might help you.
Create a function that works on an slice-like interface. Then create new types based off of a slice of your concrete types.
Hopefully, the code is more clear than my description. http://play.golang.org/p/TL6yxZZUWT
type IdGetter interface {
GetId(i int) int
Len() int
}
func GetIds(ig IdGetter) []int {
ids := make([]int, ig.Len())
for i := range ids {
ids[i] = ig.GetId(i)
}
return ids
}
type Foo struct{ Id int }
type Bar struct{ Id int }
type FooIdGetter []Foo
func (f FooIdGetter) GetId(i int) int {
return f[i].Id
}
func (f FooIdGetter) Len() int {
return len(f)
}
type BarIdGetter []Bar
func (b BarIdGetter) GetId(i int) int {
return b[i].Id
}
func (b BarIdGetter) Len() int {
return len(b)
}
func main() {
var f = []Foo{{5}, {6}, {7}}
var b = []Bar{{10}, {11}, {12}}
fmt.Println("foo ids:", GetIds(FooIdGetter(f)))
fmt.Println("bar ids:", GetIds(BarIdGetter(b)))
}
There is still a bit more boilerplate than is pleasant, (Go generics... someday). It's greatest advantage is that new methods do not need to be added to Foo, Bar, or any future type you may need to work with.

Resources