cheekybits / genny

Elegant generics for Go
MIT License
1.71k stars 126 forks source link

support extending generic type #31

Open nati opened 7 years ago

nati commented 7 years ago

Hi. Thank you for your providing a neat package!

I would like to extend generic type like this Java example so that I can call method in the type.

public class Bar<T extends B & java.io.Serializable> {}

and I find I could do similar stuff with Genny. but it is a bit hacky way.

package genny

import "bytes"

// NOTE: this is how easy it is to define a generic type

//Something ..
type Something interface { //generic.Type
    String() string //generic.Type
} //generic.Type

// SomethingQueue is a queue of Somethings.
type SomethingQueue struct {
    items []Something
}

func NewSomethingQueue() *SomethingQueue {
    return &SomethingQueue{items: make([]Something, 0)}
}

func (q *SomethingQueue) Push(item Something) {
    q.items = append(q.items, item)
}

func (q *SomethingQueue) Pop() Something {
    item := q.items[0]
    q.items = q.items[1:]
    return item
}

func (q *SomethingQueue) String() string {
    var buf bytes.Buffer
    for _, item := range q.items {
        buf.WriteString(item.String())
    }
    return buf.String()
}

How about support this use case officially, and add "generic.ignore" for ignoring line?

ThinCats commented 5 years ago

I think it's a bit difficult to implement the same syntax as Java :), but we can use the golang style to nest the interface.

In your example, you can use existed interface defined in other packages

(The following code will work on genny df3d48a)

package genny
import (
  "bytes"
  "fmt"
)

type Something interface {
  fmt.Stringer
}

type SomethingQueue struct {
  items []Something
}

...

func (q *SomethingQueue) String() string {
  var buf bytes.Buffer
    for _, item := range q.items {
        buf.WriteString(item.String())
    }
    return buf.String()
}

It's also easy to extend other interfaces, like json.Marshaler. We can also nest with generic.Type to indicate the interface is generic

type Something interface {
  generic.Type

  json.Marshaler
  fmt.Stringer
}

We can do this because genney will replace the string identifier of the file, regardless of its actual type. In this way, we can extend more complex types while being type-safe.

I have summarized several common usages used in my toy project.

  1. Type Object extends to struct (has fields)

    
    type Object struct {
    generic.Type
    
    name string
    age int
    }

// Now you can use it in function without compile errors func doSomeThing(o *Object) (string, int) { ... return o.name, o.age }

2. Type `Object` extends to interface (has method)
```go
type Object interface {
  generic.Type // can be removed

  run() Object
  stop() Object
}

// use it as normal interface
func operate(o Object) {
  o.run().stop()
}
  1. Mock package
    
    // file1: demo.go
    type mockPackage struct {
    // package function
    Log(...interface{}) error
    // package exported variable
    ColorRed int
    ColorGreen int
    // Not find a way to mock exported Type
    }

var PackageName mockPackage

// file2: demo_template.go // Use in another file func normalFunc() { PackageName.Log("Hello", "Good") color := PackageName.ColorGreen ... }

// use command // geeny -in=demo_template.go -out=gen_demo.go gen "PackageName=mypkg"


Hope to be helpful :)