なぜGo言語 (golang) はよい言語なのか・Goでプログラムを書くべき理由

このエントリーをはてなブックマークに追加

Go言語 (golang)の良い点

結論としてはGo言語には以下のようないくつかの長所があり、現実路線で非常にバランスがとれた言語だと思います。 これらの長所のために失われたメリットも当然いくつもありますが、一定程度以上の規模のプロジェクトで利用する言語の選択肢としては現存するプログラミング言語の中では一番か二番目によいのではないかと思います。

シンプルでバランスの取れた言語仕様

なぜGoは”悪い”言語なのか

Goに対する批判は数多く存在します。それのどれにもきちんとした理由はあると思います。主な批判は大きく

に集約されるように思います。

Generics (template)がない

Go言語(golang)にはGenericsはありませんJavaのGeneric TypesとかC++のテンプレートで書けるようなことはGoでは書けません。 ただ配列(スライス), mapについては特別に言語でサポートされているのでJavaやC++で総称型を使うケースの9割ぐらいはカバーされるとは思います。Java1.5より前のJavaのように連想配列や可変長配列を使うのに常にキャストが必要になったりはしません。そのためGenericsがサポートされていないことはそれほど大きな問題にはなりません。

上述のFAQにかかれている通り、GenericsをサポートしないことによってGo(golang)の型システムはシンプルに保たれています。 C++のテンプレートの意味不明なコンパイルエラーに悩まされることはGo言語(golang)ではありません。 またGenericsも継承と同じように本来使われるべきでない場所で乱用される言語機能の1つだと思います。Go/Javaのinterfaceに相当するもので十分なものに無意味にGenerics (template)が使われていて保守性・可読性の低いコードは特にC++でよく見かけます。そのためカスタムのコンテナを定義するのにGenericsがないのは不便だけれど、Genericsが存在することで生じる複雑性や乱用による不利益を避ける方を選んだのは悪いことではなかったと思います。

一方で、mapと配列以外のカスタムのコンテナが使いたい時にはGenericsがないのは不便なのも確かなので、将来うまい落とし所がみつかることを期待したいです(Generics may well be added at some point.)。 現時点でどうしてもGenericsのようなことがやりたければ、go generateでコードを自動生成するのも1つの手ではあります。あまり多用しすぎない方がよいのかなとは思いますが。

継承がない

Goには継承はありませんそもそも継承はプログラミング言語にあまり必要ない機能だと思います。 継承が本当に有益なこともありますが、経験上大半のケースでは設計を手抜きするために継承が使われていて、結果長い目で見た際のreadabilityやmaintainabilityが著しく劣化してしまっていることが多いと思います。Composition over inheritanceリスコフの置換原則のような基本的な原則が守られておらず(そもそも多くの人は名前すら知らない)、単に一部のコードをクラス間で共有するために継承が使われていて可読性が著しく低いコードもよく目にします。

そのため、そもそもプログラミング言語が継承をサポートしないというのは非常に良いデザインだなと思います。継承が非常に有益な場合も稀にあるもの分かりますが、大規模プロジェクトにおいては正しく使われない害のほうが確実に大きいです。

Errors as values (例外が推奨されない)

Go言語のFAQにあるように、Goには例外がありません。panic, recoverで例外と同じようなことはできますが、Javaの例外のように気軽に使ってはなりません。個人的にはこのFAQにかかれていることには概ね同意します。 例外で返されたエラーを try {...} catch (Exception e) {...} みたいに処理しないといけないのは無意味に複雑なように思います。 それだけならよいですが、例外が発生するとコードが想定外の順序で実行されて困ったり、何故かこのコードが実行されないなと思ったら、その前に例外で大域脱出していて、しかもその例外が予想外のところでcatchされ握りつぶされていたり、と例外を大規模なプロジェクトの中で正しく扱うのは中々に困難だと思います。 Goの例外は極力使わず、エラーを値として扱うポリシーはよいもの(特に大規模なプロジェクトで、エラーハンドリングが大切なプロジェクトでは)だと思います。

ただ一方で、ちょっとした使い捨ての便利ツールを書く場合や、とりあえずプロトタイプで正常系だけ書きたい時、 あるいは異常が発生したらプログラムを停止してしまって良いような起動時の初期化処理を書く時には、正直Goのエラーハンドリングはかなり面倒くさいです。 こういうタイプのコードでは外部ライブラリの呼び出しやファイル、データベースなどの外部リソースへのアクセスが大きな割合を占めます。そして、そうした処理はほとんどの場合 error が発生しうるのでそれぞれの処理に対してエラーハンドリングを行う必要があります。 場合によってはコードのかなりの割合の行が

if err != nil {
	return nil, err
}

の繰り返しで占められてしまうこともあるでしょう。これに対しては根本的な解決策はないように思います。エラーが発生した場合はエラーメッセージを出力して処理を中断してしまって問題ない

には例外の方が便利であり、正直Go言語ではあまり効率的にコードが書けないような気がします。個人的にはそういう用途にはPythonなどを使うのが正しい解決策のように思えます。何でもGoで書く必要はないのですから。 型の存在すら簡単なプロトタイピングをするのには少し鬱陶しいですしどんな言語も適材適所だと思います。

非知的なプログラマのためにデザインされている (Designed for non-intelligent programmers)

Goは知的でないプログラマのためにデザインされていると批判されることがあります。そして「知的でないプログラマ」のためにデザインされているというのは指摘自体は正しいと思います。実際Rob PikeもFrom Parallel to Concurrentの20:40~で次のように述べています

The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.

要するにGoogle社員のような、研究者ではなく大学を出たばかりでC++, Java, Pythonの経験しかない「素晴らしいプログラミング言語」を理解する能力が欠けているエンジニアでも簡単に理解し、大きなシステムを構築するのに使えるようにGo言語はデザインされているのです。

Goは言語デザインとして正しく使うのが難しい、乱用するとプログラムを無意味に複雑にしてしまう機能が排除されています(高度な型システム・継承・Generics・例外・イベントモデルによる並行処理など)。 そのため、ある意味ではGoは「知的な」プログラマにとっては面白みに欠く言語だと思います。実際、Goを勉強しても概念的には目新しいものはなくて勉強することそのもので何かが学べるとは僕は全く思いません。 一方でプログラミング言語を習得することのゴールは、それを使って何か大規模なソフトウェアをチームで作ることであるのだと思います。 チームで大規模なソフトウェアを開発することはそれ自体が非常に難しいことです。そのゴールのためにプログラミング言語自体はシンプルに保ち、それ以外の部分により多くの労力をさけるようにするというのは僕は正しい判断だと思います。

golang 関連記事

このエントリーをはてなブックマークに追加
Home