Source: metagoを作った話
Files changes: 1
- go/metago/README.md (+113, -0)
vvakame -2019-08-23 16:44:55
metagoはGo言語向けのメタプログラミングライブラリです。
考え方のベースとしてwireのシグニチャを定義し実装は機械的に生成する考え方と、VのReflection via codegenのホスト言語の構文でメタ構造を書く、というのを使っています。
本ライブラリはある程度動きますが、現時点ではトップレベルの定義を生成したり動的な名前のメソッドを生成したりすることはできません。
テストケースも圧倒的に不足しているため実用レベルに達しているかといわれると疑問があります。
でも面白いよ!
モチベーション
Goは言語自体の持つ型の表現力が弱く、ボイラープレートなコードをたくさん書くことになりがちです。
そこで、何らかのデータを元にGoのコードを自動生成しよう!というアイディアに至るのに時間はかかりません。
一番最初に思いつくであろう、GoのコードをASTで組み立てる戦略は破滅的にめんどくさいです。
これは、生成したいと思っているGoのコードとASTを組み立てるコードにまったくもって相似ではないからです。
次のアイディアとして、Goのコードをテキストとして組み立てるものがあります。
jwgでは Printf
を使ってソースコードを生成しています。
gqlgenでは text/template
を使ってソースコードを生成しています。
これは、ASTを使って組み立てるのに比べるとだいぶ仕上がりが想像しやすいです。
一方でIDEからの支援が得にくく、出力後のコードがvalidなコードかというのは出力してみるまでわかりません。
新しいアプローチとして、本ライブラリ metago を考え、実装しました。
metagoでは生成するべきGoのコードをGoのコードで書きます。
鋳型になるGoコードのASTをこねこねして、ほしいGoコードに変換するイメージです。
これならば、IDEの支援を今までに比べると圧倒的に楽に実装することができます。
metago について
metagoでは、実際のソースコード中にマーカーを仕込んでいき最終的なソースコードを生成します。
あるオブジェクトのフィールド名と値を出力するテンプレートは次のようになります。
1 | //+build metago |
これをmetagoで処理すると次のコードが得られます。
実際にプログラムとして動作させるのはこちらの生成されたコードです。
1 | // Code generated by metago. DO NOT EDIT. |
reflectパッケージに少し似ています。
どういったことができるかというのはtestbedディレクトリを見てみてください。
主なfeatureとして…
mv := metago.ValueOf(obj)
によってmetago.Value
な値を取得for _, mf := range mv.Fields()
によって各フィールドに対する処理を展開・記述mf.Name()
によってフィールド名の取得mf.Value()
によってフィールドの値の取得mf.StructTagGet("json")
などでstructのタグの取得mf.Value().(time.Time)
といった型アサートとif文の組み合わせによるフィールドの型毎の処理の振り分け- type switchもサポート
- インラインテンプレート(第一引数が
mv metago.Value
の関数)の利用
などに対応しています。
metago のインストールと実行
1 | $ go get -u github.com/vvakame/metago/cmd/metago |