My External Storage

Oct 23, 2020 - 8 minute read - Comments - go poem

Goらしさとは何なのか考える

「Goらしさ」や「Goに入ってはGoに従え」というけれど、「Goらしい」って一体なんだろう?と考えてみる。

TL;DR

後半は完全に私見の域を出ない&&身も蓋もない結論なので、最初に参考情報だけまとめておく。

「Goらしさ」が何を目指しているのか、何を目指す考えが「Goらしさ」なのか知りたいならば、まず言語思想・設計思想を知るべきだろう。 言語思想についてまとめられている文書は次の情報だ。

一言でいうと「Goらしさ」とは「Simplicity(簡潔性)」だ。それは次のミッションを実現するための手段だ。

  • GoのMission
    • Creating software at scale
    • Running software at scale

Goはあくまでミッションを「スケール」と書いていて、単純な「スピード(開発速度)」ではないところが重要なのではないか。 日本語でまとめられている文書で言うと@songmuさんの記事を読むのが良いと思う。

書籍で「Goらしさ」や「Simplicity(簡潔性)」を学ぶならば次の書籍だろう。

言語思想や言語哲学に沿った実装を行えばその言語がもつパフォーマンスや表現力をフルに扱うことができるはずだ。 ここでいうパフォーマンスは実行時の性能だけを指すのではなく、開発中の生産性なども含む。 そのためには当たり前の結論でしかないが、次のような研鑽を積むしかないのかなと思う。

  • 言語思想や哲学を学ぶ
  • 標準パッケージ、有名なOSSや著名なGopherのコードからGopher仕草を盗み取る
  • 一般的なプログラミング設計原則やエンジニアリングのプラクティスを学ぶ

Goはソフトウェアエンジニアリングを行なうための言語である1ため、プラクティスや設計原則に基づいた実装をすることが最適なコードとなる。
ただ、エラー処理が雑になりがち2な例外機能や、”継承”のような「大半は悪い設計3」になるようなデザインは言語としてサポートされていない。
あまり考えずに既知のアプローチを試すと失敗するのでそこは言語仕様や哲学の学習が必要となる。 上記の書籍以外にもまずは次の文書から読むと良いだろう。

その他の基礎情報は次の記事にまとめてある。

どうなると「Goらしさ」があるのか?

まず、「Go言語らしくxxxxする」と言うとき、Mustなこと(全員一致なGoらしさ)とWantなこと(人によりそうなGoらしさ)があると思う。 そして、Wantな部分は結構経験によったり、人によって解釈が違うこともあるので難しい。

MustなGoらしさ

まず必ず守らなければいけないGoらしさがある。

  • gofmtをかける
  • panicを使わない
  • errorを握りつぶさない

gofmtをかけたりするpanicを避けることに異論があるGopherはいないだろう。

WantなGoらしさ

上記のような当たり前のプラクティスとは別に意見が分かれるような「Goらしさ」もあるのかなと思う。

たとえばAssertion

たとえば私はtestifyassert.Equalなどが定義されている3rdパーティのライブラリ)を使わない。

私はtestingパッケージ以外のDSLを覚えるのが嫌いだからだ。 そもそもなぜ標準パッケージでassertXXX関数が提供されていないかは書籍「プログラミング言語Go」の11.2.5節で述べられている。

…ただ私はgomock(そこそこリッチなモックオブジェクトを作れるgoogleのライブラリ)は使っている。

gomockも独自DSLのメソッドチェーンで呼び出し回数などを指定することができる。 「さっきDSL覚えるの嫌って言っていたじゃん」と言われるとその通りである。

  • assertユーティリティが無くてもテストは書ける(困ったことがない)
    • 依存を増やしてまでほしいと思わない
    • 組織の中でhelper関数を作って組織としての知識を醸成するのはCreating software at scaleに則っていると思う
  • モックを毎回自前で作るのはさすがにしんどい(困ったことがある)

自分の中では上記のような判断基準があるのだけれど、「いやNotContains関数ないと困るでしょ!!」のような意見もあると思う。

たとえばFunctional Option Pattern

Goではオーバーロードができないので、引数違いの同名関数、同名メソッドが定義できない。 そこでGoではFunctional Option Patternがよく使われる。

これはOpen-Closedの原則に則っている実装パターンだ。 変更に対して関数のシグネチャは閉じているが、オプションの追加という拡張に対しては開いている。 すごいシンプルで私はGoらしくて好きなパターンだが、クロージャを使っていたりするので、もしかしたら「どこがシンプル?」という意見もあるかもしれない。

同様にmiddleware/interceptorパターンも初期化のためのオブジェクトを要するようになると人によっては読みにくいかもしれない。

func NewLoggingMiddeware(l *log.Logger) func(http.Handler) http.Handler {
  return func(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
      buf := &bytes.Buffer{}
      rww := NewRwWrapper(w, buf)
      next.ServeHTTP(rww, r)
      l.Printf("%s", buf)
    })
  }
}

また、変数の参照可能性を利用した下記のようなクロージャの宣言も読み手によってはシンプルには見えないかもしれない。

// プログログラミング言語Go 5.6節より引用
func squares() func() int {
  var x int
  return func() int {
    x++
    return x * x
  }
}

// f = squares()
// f() // return 1
// f() // return 4
// f() // return 6
// f() // return 16

どれも言語仕様でサポートされているのでGopherとして「Goらしく読みやすい」と考えていいと思うのが私の考え方だ。 こういう考えかたの延長で言うと、2021年以降はGoらしさ with Genericsでいろいろなパターンやベストプラクティスを更新しないといけない。

Goらしい書き方の参考

Goらしい書き方の参考になるのは標準pkgなどの「そこ」にあるコードだが、どのように考えるべきかは書籍や先人たちの知恵に頼るのがよいだろう。 プログラミング言語Goは文法の他にも言語仕様やちょっとしたプラクティス、設計背景も記載されており、「Goらしさ」を学ぶ上でとても大事な書籍だ。

「Go言語によるWebアプリケーション開発」はだいぶ内容が古い。 が、訳者でgooglerの鵜飼さんがGoっぽくない原著のコードをかなり指摘したり付録で修正している。 「なぜGoらしくないか」の解説もあるので読みがいがある。

その鵜飼さんやGooglerが書いたスライドも参考になるだろう。

一般の設計原則の他に、UNIXという考え方もSimplicity(簡潔性)を考える上で重要だと思う。

反証。Web開発やCLI作りでGoらしさは必ずしも正しいのか

とはいえ、Goらしさに固執することで生産性が落ちるならば本末転倒なわけで難しい。
Web開発ではDDDやクリーンアーキテクチャなどの考えやgRPC、OpenAPIやWebフレームワークを採用することもあるだろう。
Go以外の有効なプラクティスに寄せることでGoらしさが守られなくても生産性が向上するならばそれは問題ないのでないだろうか。

ライブラリなどを作る場合は厳守しておいたほうがよい。Goのライブラリならばライブラリ利用者に「標準pkg」のような使い心地を提供したほうがよく、Goらしさが重要だろう。 一方で何かしらのアプリケーションを作る際は作る対象・あるいはチーム内で合意できたアプローチをしてもよい。というのが私の今の気持ちだ。

たとえばutilなんてパッケージ名はGoでは良くないとされるが、Googleが作ったwebサーバを見ても、pkg/timeutilsパッケージなんかがあり、ほほーん?となる。 要はバランスってやつだ。きっと。

終わりに

なにか落としどころが微妙な散文になってしまったが、結局「どうしてそうなっているか、あるいはそうするかを考えながらたくさんコードを読んだり書いたりしてバランスを取ろうね」という身も蓋もない結論になってしまった。

参考情報

余談

IT業界では「お気持ち」な記事をポエムっていうけれど、本当は「エッセイ」なんじゃないかなーと思ったりする。

1 1編の詩。韻文作品。 2 詩的な文章。詩的だが中身のない文章・発言を揶揄 (やゆ) していうこともある。

「中身のない文章・発言」だと自虐的に宣言するからポエムなんだろうか?

1 自由な形式で意見・感想などを述べた散文。随筆。随想。 2 特定の主題について述べる試論。小論文。論説。

そういう意味だと、中身はあるつもりで書いているので、「自由な形式で意見・感想などを述べた散文。随筆。随想」という意味のエッセイということにしたい。 (といいつつタグはポエムでつくってしまっている。)


  1. Go at Google: Language Design in the Service of Software Engineering 3. Go at Google ↩︎

  2. 基底Exceptionクラスでtry-catchしたりしてませんか。 ↩︎

  3. 継承する前にEffective Javaを読もう。項目16 「継承よりコンポジションを選ぶ」 [プログラマー現役続行] ↩︎

関連記事