4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Genny: Elegant generics for Go

Last updated at Posted at 2017-03-10

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()
}

4
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?