GoでGenericsの話題で盛り上がってるのに刺激されて、Gennyを使ってみた所感を書いてみます。
Elegant generics for Go
golang と Generics と吾 の記事で、
@nobonobo さんがGennyの例を紹介されています。
Goでコード生成
Goの言語設計は、シンプルさ読みやすさ重視なので他の言語で用意されている便利な機能が
なくて物足りなくなることもあります。そういった便利機能は書くときに便利な機能で、読みやすさを損なうことが多いです。実際に実務で使って見ると、なくてもそんなに困らないし、読みやすさの恩恵の方が大きいと感じます。
それでも、やっぱりBoilerplate Codeはだるいし、interfaceはオーバーヘッドがあるしで
コード生成するというのが落としどころではないかと思ってます。
コード生成辛い
text/templateとかでコード生成しようかと思うけど、辛いです。
コード生成用のテンプレートを書くのが辛い。IDLの支援はないし、テストはやりにくい。
生成されたコードが汚かったり、読みにくかったり、最適なコードを生成するのが難しかったり、、
そこでGenny
Gennyは完璧ではないですけど、この辺を楽にしてくれます。
Gennyでは、GoのコードからGoのコードを生成するので、IDLの支援を受けられれますし
試験も比較的描きやすいのです。
仕掛けは、generic.Type型を使ってコードを書くと、ASTをパースしてコードを生成してくれます。
type KeyType generic.Type
type ValueType generic.Type
READMEの例
package queue
import "github.com/cheekybits/genny/generic"
// NOTE: this is how easy it is to define a generic type
type Something 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
}
が
cat source.go | genny gen "Something=string"
で
// This file was automatically generated by genny.
// Any changes will be lost if this file is regenerated.
// see https://github.com/cheekybits/genny
package queue
// StringQueue is a queue of Strings.
type StringQueue struct {
items []string
}
func NewStringQueue() *StringQueue {
return &StringQueue{items: make([]string, 0)}
}
func (q *StringQueue) Push(item string) {
q.items = append(q.items, item)
}
func (q *StringQueue) Pop() string {
item := q.items[0]
q.items = q.items[1:]
return item
}
でも万能ではないです、、
使ってみたのですが、
generic.Typeを操作したり、メソッドを読んだりできないのが不便でした。
generic.Typeはinterface Xを実装すること とか書けるといいのですけど、、
(なので未だに泣く泣くtext/templateを使ってます)
つまりは、こういう記法をサポートしてほしいなと思います、、
まぁ書いてPR送ればいいんでしょうけど。
//Something ..
//genny.GenericType
type Something interface {
Method1() Something
}
追記
無理やりこうすればできるっぽい、、hackyですけど
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()
}