Go言語は、LLMにとって最適な言語である
これまで「Goは冗長だ」「表現力が低い」と批判されてきたポイントが、皮肉にもAI時代には最大の武器に反転しようとしています。
Goのコードは誰が書いても似たような構造になり、予測可能性が極限まで高まります。この仕様から、LLMにコードを書かせるなら、Go言語が最も効率的であるという説が生まれています。
なぜGoがAIエージェントに最適なのか?
1. 予測可能性の高さ(抽象化の限界)
-
Goには「やり方が一つしかない」という設計思想があり、コードの書き方が標準化されています。
-
人間が嫌う「抽象化の低さ」や「冗長なボイラープレート」は、LLMにとってはむしろ予測しやすさに繋がり、正確なコードを生成しやすくなります。
2. 安定したエコシステム
-
過去10年間、言語仕様に破壊的な変更がほとんどなく、フレームワークの流行り廃り(チャーン)も少ないため、LLMの学習データが古くなりません。
-
PythonやTypeScriptのように、似たような機能を持つライブラリが乱立していないため、LLMが迷いにくいです。
3. 高速な反復(Iteration)
- コンパイルとテストの実行が非常に速いため、AIエージェントが「コード生成 → テスト実行 → エラー修正」というサイクルを高速に回すのに適しています。
4. 静的型付けと並行処理
- 型定義があることでLLMの出力を検証しやすく、Goの並行処理(Goroutine/Channel)はC++などに比べてバグを生みにくい構造になっています。
他の言語との比較
-
Java: 「学習データが豊富で型システムも優れている」という意見がある一方、「古いバージョンやアノテーション(魔法のような動作)が多く、LLMにとっては予測しづらい」という反論があります。
-
Rust: 「コンパイラのフィードバックが強力なのでAIに適している」という意見がある一方、「ライブラリの変化が激しく、コンパイルが遅いため、AIの試行錯誤には向かない」との指摘もあります。
-
Python: 「AI分野の主流だが、動的型付けで自由度が高すぎるため、エージェントが生成するコードとしてはエントロピー(不確実性)が高くなりがちである」と評されています。
Goの抽象化の限界とは
GoがAIエージェントに最適だと言われている理由は、コードが誰(あるいはどのAI)にとっても予測可能になるからです。
1. エラー処理の「泥臭さ」
多くの言語(Java, Python, JSなど)では、try-catchを使ってエラーを遠くの階層へ丸投げ(抽象化)できます。しかし、Goには例外がありません。
Pythonの場合:
# Python
# どこでエラーが起きるか、関数のシグネチャからは不明
def process_data():
try:
data = fetch_from_api() # ここで例外が出るかもしれない
save_to_db(data) # ここでも出るかもしれない
except Exception as e:
log_error(e) # まとめて処理(どこで起きたか追いにくい)
Goのやり方では、全ての関数で $if err != nil$ を愚直に書きます。
Goの場合:
# Go
func processData() error {
data, err := fetchFromAPI()
if err != nil {
return fmt.Errorf("API取得失敗: %w", err)
}
err = saveToDB(data)
if err != nil {
return fmt.Errorf("DB保存失敗: %w", err)
}
return nil
}
「エラーが発生した事実」を隠蔽してどこか一箇所でまとめて処理する、という抽象化ができません。
LLMは「どこでエラーが起き、どう戻るか」をコード上で1行ずつ追えるため、推論のミスが減ります。
2. 継承がない
Goには、JavaやC++のような複雑なクラス継承がありません。
Goのやり方は、構造体の中に別の構造体を入れる(埋め込み)だけです。
Javaの場合:
// Java
// 親の親のクラスで何が定義されているか、別のファイルを見に行かないと不明
class Dog extends Mammal {
void bark() {
super.breathe(); // Mammal? Animal? どこで定義された?
System.out.println("Woof!");
}
}
Goの場合:
# Go
type Animal struct {
Species string
}
type Dog struct {
Animal // 埋め込み(継承ではない)
Name string
}
// 構造がフラットで、フィールドの出所が明確
d := Dog{Animal: Animal{Species: "Canine"}, Name: "Pochi"}
コードの依存関係がフラットになるため、AIが「今この変数がどこで定義されたか」を見失いにくくなります。
3. アノテーションのような依存関係がない
JavaのSpring Bootなどでは、@Autowiredと書くだけで依存関係が楽に解決されます。これは非常に高度な抽象化です。
Java(Spring)の場合:
// Java
@Service
public class UserService {
@Autowired
private UserRepository repository; // どこからこの実体が来るかはフレームワークの「魔法」
}
Goのやり方では、必要なものは全て関数の引数として明示的に渡します。
# Go
type UserService struct {
repo UserRepository
}
// 外部から何が必要か、関数の引数を見れば一目瞭然
func NewUserService(r UserRepository) *UserService {
return &UserService{repo: r}
}
LLMは「見えているコードが全て」であると確信できます。外部の設定ファイルやフレームワークの暗黙のルールを読み取る必要がありません。
まとめ
| 特徴 | 人間(上級者)の感想 | AIエージェントの感想 |
|---|---|---|
| ボイラープレート | 「冗長で退屈だ」 | 「パターンが明確で生成しやすい」 |
| 低い抽象度 | 「もっと賢い書き方がしたい」 | 「コードの意図が透明で間違えない」 |
| 一貫性 | 「表現力が低い」 | 「学習データとの乖離が少ない」 |
Goのコードが誰が書いても同じに見えるのは、自由度を奪う欠点ではなく、AIとの協調における最大の利点となりました。
「エラーハンドリングが面倒」「継承がなくて不便」というベテランエンジニアの嘆きは、AIエージェントにとっては「迷路の出口が常に示されている」という福音に変わります。
「GoはAI時代に最適である」という説は、プログラミング言語の評価軸が「人間が書きやすいか」から「AIが正確に扱えるか」へとシフトしている兆しと言えるでしょう。
参考
Discussion