㊙️

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が正確に扱えるか」へとシフトしている兆しと言えるでしょう。


参考

https://news.ycombinator.com/item?id=47222270

Discussion