はじめに
こんにちは、ひるげです。
最近『初めてのGo言語』という本でGoを勉強しています。
Goというと速い、並行処理が得意、といった点がよく語られますが、実際に触ってみて一番刺さったのはそこではありませんでした。
私が好きなのは「Clear is better than clever」。
巧妙に短く書くことよりも明快さを優先する、という言語思想です。
この記事では、Goを知らない人向けに、Goがどういった思想を持っている言語なのかをざっくりと紹介してみます。
「簡潔さよりも明快さ」とは:3つの具体例
短く賢く書けることよりも、多少長くなってもいいので読んだ時に飲み込みやすいことを優先する。
これが、Goが繰り返し掲げている基本的な価値観です。
ではこれは具体的にどうやって実現されているのでしょうか。
簡単な具体例を3つみてみましょう。
1. 暗黙の型変換がない
例えば多くの言語では、整数と小数を足すと言語側がよしなに型を合わせてくれます。
Goはこれをやりません。足したいなら、どちらかを明示的に変換しないとコンパイルが通らないようになっています。
i := 3 // 整数
f := 1.5 // 小数
// fmt.Println(i + f) // コンパイルエラー: mismatched types int and float64
fmt.Println(float64(i) + f) // 4.5 明示的に変換すれば足せる
書くのは少し面倒かもしれませんが、コードを読むときには「ここで数値の型が変わっているな」と目で追える作りになっています。
「裏でこっそり変換されて桁が落ちる」といった事故も、これで起きにくくなっています。
2. 使っていない変数やimportがあるとコンパイルエラー
Goでは「とりあえず何でも用意しておく」ができません。
使っていない外部ライブラリの読み込みや、宣言したのに使っていない変数があると、その時点でエラーになってビルドが止まります。
import (
"fmt"
"os" // "os" imported and not used → コンパイルエラー
)
func main() {
x := 10 // x declared and not used → コンパイルエラー
fmt.Println("hello")
}
ちょっと神経質なようにも思えますが、裏を返すと、コードに書いてあるものは全部どこかで使われていると信じて読めます。使われていない宣言がノイズとして紛れ込む余地がないので、余計な心配をせずに読み進められます。
3. 例外がない
多くの言語には例外という仕組みがあります。
エラーが起きうる処理を書くとき、その場で対処を書かなくても、異常が起きればプログラムが勝手に中断して、どこか外側で対処してくれる。書く量は減りますが、その1行を見ただけでは「ここでエラーが起きうるのか」「起きたら誰が対処するのか」が分かりません。
一方Goには例外がありません。エラーが起きうる処理は、結果とエラーの2つを返します。呼び出した側は、そのエラーへの対処をその場に必ず書きます。
f, err := os.Open("config.yaml")
if err != nil {
// エラー時にどうするかを、ここに明示的に書く
fmt.Println("設定ファイルを開けませんでした:", err)
return
}
// ここまで来たならエラーはない。安心して f を使う
このif err != nil はGoのコードを見ていると何度も何度も登場します。
これを毎回書く分、コードは長く冗長になるわけですが、その代わり、どの行でエラーが起きうるのか、起きたら何をするのかが、すべてその場に書いてあります。
エラーに対してどう対処するかを省略させないことで、コードが明快になっているわけです。
なぜGoの思想が自分に刺さったのか
私はコードを読むとき、「まずこれは何をやっているコードなのか」を理解したいタイプです。
これまで触ってきた言語だと、規約や短い書き方に乗れば速く書ける代わりに、なぜこれで動くのかがすぐには見えないことがありました。
Railsなどは特にそうで、使いこなせば強力な反面、初心者のうちは「この記述でなぜ動くんだ」と手の止まる場面が多かったです。
Goはそういった黒魔術的な書き方ができません。
書いてあることと実際の挙動のあいだにズレがほとんどないので、ざっと読んでいくだけで中身をつかめます。
AI時代におけるGoのありがたい特性
簡潔さより明快さを優先するこの姿勢は、コードを読む時間が増えていくこれからの開発とよく噛み合っていると思います。
もう体感されている方も多いと思いますが、AIがコードを書く比重が上がると、自分で書く時間より、AIが書いたコードを読んでレビューする時間のほうが長くなっていきます。もはや、自分ではほとんどコードを書いていないという方もザラにいるのではないでしょうか。そのときGoなら、何が書いてあるかをパッとつかめるので、低い負荷で速くレビューを回せます。
そしてGoには、読みやすいコードが自動的に生産される仕組みが他にもあります。
たとえば go fmt という公式のフォーマッタがあり、これを通すとコードの見た目が一意な形に揃います。インデントは空白かタブか、改行はどこで入れるか、といった議論がそもそも発生しないので、レビューでは中身だけを見ればよくなります。
また、defer という、認知の荷物を減らしてくれる仕組みもあります。
defer は、簡単にいうと「後片付けを約束する仕組み」です。ファイルを開いた直後に、「関数を抜けるときにこのファイルを閉じる」と一行書いておけます。
f, err := os.Open("config.yaml")
if err != nil {
return
}
defer f.Close() // 「この関数を抜けるとき f を閉じる」と先に予約しておく
// この先でどこで return しても f.Close() は必ず実行される
defer を使うと、後片付けの存在を関数の最後まで覚えておかなくて済みます。
『リーダブル・コード』を読んだことがある方はわかると思いますが、読み手が覚えておくべきことを極力減らし、認知の荷物を軽くしてあげことで、コードはグッと読みやすくなります。
defer は、先に後片付けを確約することで、認知負荷を下げてくれるありがたい仕組みなんです。
...他にも色々とあるわけですが、こうした細かい配慮の積み重ねで、Goは人間がコードを読むコストを下げてくれるのです。
AIと一緒に開発する時間が増えるほど、この思想のありがたみはじわじわ効いてくるはずです!
おわりに
ということで今回は、Goの思想「Clear is better than clever」について紹介してきました。
一見冗長で地味にも見えますが、何より明快であることを重視するこのスタンスは、自分やAIが書いたコードを読む時間が増えてきた今の潮流を考えると、かなりありがたいものだと思います。
速さや並行処理といった魅力だけでなく、今回述べた基本思想を味わうためにも、軽い気持ちでGoの世界へ一歩踏み出してみてはいかがでしょうか。
では、ここまで読んでいただきありがとうございました!