第1章 Mojo とは何か・位置づけ
1.1 この章で学ぶこと
この章で学ぶ内容は以下のとおりです。
学ぶこと |
ポイント |
|---|---|
Mojo とは何か |
Python に似た構文を持つ、コンパイル型の高性能言語 |
Python との違いの第一印象 |
変数・関数・構造体の書き方が Python とどう異なるか |
Mojo の 3 つの強みの入口 |
型・所有権・Python interop のキーワードを掴む |
この本の読み方 |
どの章で何を学ぶかの地図 |
1.2 Mojo とは何か
Mojo は Python に似た構文を持つ、コンパイル型のシステムプログラミング言語です。 2023 年 5 月に Modular(Modular Inc.)が公開しました。 ただし当初はクローズドソースであり、標準ライブラリは 2024 年 3 月にオープンソース化されました。 言語コンパイラ自体のオープンソース化は 2026 年秋が予定されています。
「Python の書き心地のまま、C と同等のパフォーマンスを」というビジョンで設計されており、次の 2 点を同時に実現しています。
Python との相互運用:既存の Python ライブラリ(NumPy、PyTorch、MLX など)をそのまま呼び出せる
コンパイル時最適化:型、所有権、メタプログラミングにより、実行前に機械語レベルで最適化できる
Python を知っているなら、最初の数行はそのまま読めます。 ただし一歩踏み込むと、Mojo は Python とは異なる設計の言語です。 本章ではその「入り口の似ているところ」と「踏み込んだ先の違い」を並べて紹介します。
Python |
Mojo |
|
|---|---|---|
実行方法 |
インタープリタが 1 行ずつ解釈 |
コンパイルして機械語を生成 |
型の決定 |
実行時(動的) |
コンパイル時(静的) |
メモリ管理 |
GC(参照カウント + サイクル検出) |
所有権 + 参照カウント(決定的タイミング) |
Python との相互運用 |
ネイティブ |
|
GPU 対応 |
外部ライブラリ経由 |
言語レベルでサポート |
図1-1: Python と Mojo の特性比較
左(Python)はインタープリタ、動的型付け、GC(参照カウント)。 右(Mojo)はコンパイラ、静的型付け、所有権モデル、GPU 対応、高速パフォーマンス。 中央の橋(相互運用性)を経て、NumPy や PyTorch などの Python ライブラリを Mojo から直接呼び出せる。
1.3 Hello World を並べる
ここから Mojo の文法を概観していきます。 変数、関数、構造体、所有権、Python 相互運用と、各節でひとつずつ取り上げますが、この章ではあくまで「雰囲気をつかむ」ことが目的です。 細かい規則や例外は後続の各章(第6章〜第15章)で詳しく説明します。 まずはコードを眺めながら、Python との違いを感じ取ってください。
最初の Mojo コードは Python とほぼ同じに見えます。
Python:
def main():
print("Hello, Mojo")
Mojo:
def main():
print("Hello, Mojo")
リスト1-1: hello_mojo_minimal.mojo
def main(): も print(...) も構文は共通です。
しかし実行のしくみは異なります。
Python は実行時にインタープリタが 1 行ずつ解釈しますが、Mojo は事前にコンパイルして機械語を生成します。
コンパイルの流れとアセンブリは後続の章で
Mojo コードが x86-64 の機械語になるまでの経路(MLIR → LLVM IR → ネイティブバイナリ)は第2章で解説します。
実際の逆アセンブル出力を 1 命令ずつ読む演習は、第5章「最小サンプル main のアセンブリを読む」で扱います。
1.4 変数と型
Python では変数に型を書きません。
Mojo では var で宣言し、型を付けます。
Python:
x = 42
name = "Alice"
pi = 3.14159
Mojo:
var x: Int = 42
var name: String = "Alice"
var pi: Float64 = 3.14159
var y = 3.14 # 型推論(Float64 と推論される)
型は省略して推論させることもできます。
コンパイル時定数には alias を使います。
alias MAX_SIZE = 1024 # コンパイル時に確定する定数
alias PI = 3.14159265358979
alias は Python の CONSTANT = 1024(慣例的な大文字)と似ていますが、Mojo ではコンパイラが値をその場に展開します。
実行時に変数を読みにいく処理が不要になります。
Python |
Mojo |
|
|---|---|---|
変数宣言 |
|
|
型推論 |
常に動的 |
|
コンパイル時定数 |
|
|
再代入 |
常に可能 |
|
詳細は第6章「概要・関数・変数」で扱います。
1.5 関数の定義
Mojo の関数定義は Python とよく似ています。
Python:
def add(a, b):
return a + b
def greet(name: str) -> str: # 型ヒント(任意)
return f"Hello, {name}"
Mojo:
def add(a: Int, b: Int) -> Int:
return a + b
def greet(name: String) -> String:
return "Hello, " + name
Python との大きな違いは型アノテーションが実質必須である点です。 Python では型ヒントは任意ですが、Mojo ではコンパイラが型を利用するため、引数と戻り値の型を明示的に書きます。
詳細は第6章で扱います。
1.6 構造体(struct)
Python では class でデータと振る舞いをまとめます。
Mojo では struct を使います。
Python:
class Point:
def __init__(self, x: float, y: float):
self.x = x
self.y = y
def distance(self) -> float:
return (self.x**2 + self.y**2) ** 0.5
p = Point(3.0, 4.0)
print(p.distance()) # 5.0
Mojo:
struct Point:
var x: Float64
var y: Float64
def __init__(out self, x: Float64, y: Float64):
self.x = x
self.y = y
def distance(self) -> Float64:
return (self.x**2 + self.y**2) ** 0.5
def main():
var p = Point(3.0, 4.0)
print(p.distance()) # 5.0
リスト1-2: point_distance.mojo
見た目は Python の class に近いですが、重要な違いがあります。
Python |
Mojo |
|
|---|---|---|
メモリの配置 |
ヒープ(参照型) |
スタック(値型) |
フィールドの型 |
実行時に決まる(動的) |
コンパイル時に確定(静的) |
継承 |
多重継承あり |
なし( |
|
|
|
struct は値型です。
代入するとコピーが作られます。
参照を渡したいときは所有権修飾子(次節)を使います。
詳細は第9章「struct・参照型・パッケージ」で扱います。
1.7 所有権の雰囲気
Mojo では関数の引数に「この値をどう受け取るか」を修飾子で明示します。
def show(read data: List[Int]):
# data を読むだけ。コピーしない
print(data[0])
def append_zero(mut data: List[Int]):
# data を変更する。呼び出し元に反映される
data.append(0)
def consume(var data: List[Int]):
# data の所有権ごと受け取る
data.append(99)
# この関数を抜けると data は解放される
def main():
var nums: List[Int] = [1, 2, 3]
show(nums) # 1
append_zero(nums) # nums の末尾に 0 を追加
print(nums[len(nums) - 1]) # 0
consume(nums^) # 所有権を移譲して渡す
リスト1-3: ownership_conventions.mojo
修飾子 |
意味 |
Python との対応イメージ |
|---|---|---|
|
読み取り専用の参照 |
引数をそのまま受け取る(変更不可) |
|
変更可能な参照 |
リストを受け取って中身を変更する |
|
所有権を移譲して受け取る |
(Python には対応概念がない) |
Python には「この引数は変更しない」という宣言がありませんが、Mojo ではコンパイラが強制します。 うっかり変更するとコンパイルエラーになります。 詳細は第10章「値・所有権・ライフサイクル」で扱います。
1.8 Python との相互運用
Mojo から既存の Python ライブラリを呼び出せます。
from std.python import Python
def use_numpy() raises:
var np = Python.import_module("numpy")
var arr = np.array(Python.list(1.0, 2.0, 3.0, 4.0))
print(arr.mean()) # 2.5
def main() raises:
use_numpy()
リスト1-4: numpy_mean.mojo
Python.import_module() で Python のモジュールを PythonObject として受け取り、そのままメソッドを呼べます。
NumPy、PyTorch、MLX など、Python のエコシステムをそのまま活用できます。
速くなるのは「Mojo で書いた部分」だけ
Python ライブラリを呼び出す部分は Python のコードがそのまま動いています。 Mojo が高速なのは Mojo で書いたコードのみです。 「Mojo から NumPy を呼ぶだけで速くなる」わけではありません。 詳細は第13章「Python 相互運用」で扱います。
1.9 この本の読み方
本書は 3 部構成です。 各章で扱う内容と対応する Mojo の概念を示します。
章 |
タイトル |
内容 |
|---|---|---|
第1章 |
Mojo とは何か・位置づけ |
← 本章。Python との比較で全体像を掴む |
第2章 |
作者・誕生の背景・MLIR |
なぜ Mojo が生まれたか、MLIR の役割 |
第3章 |
設計思想 |
所有権・compile-time・interop の 3 本柱 |
第4章 |
Python との比較・入口 |
Python 経験者がはまる落とし穴 |
第5章 |
最小サンプルのアセンブリを読む |
|
章 |
タイトル |
内容 |
|---|---|---|
第6章 |
概要・関数・変数 |
|
第7章 |
コンパイル結果のアセンブリを読む |
前章のコードの逆アセンブル解析 |
第8章 |
型・演算子・制御・エラー |
|
第9章 |
struct・参照型・パッケージ |
値型 |
第10章 |
値・所有権・ライフサイクル |
|
第11章 |
メタプログラミング |
|
第12章 |
ポインタ・GPU・レイアウト |
|
第13章 |
Python 相互運用 |
|
第14章 |
Pythonista 向けの読み替えとコード例 |
Python の書き方を Mojo に変換するパターン集 |
第15章 |
相互運用の実例・導入判断・まとめ |
NumPy 連携の実例、Mojo を使うべき判断基準 |
章 |
タイトル |
内容 |
|---|---|---|
第16〜22章 |
microgpt の解説 |
Karpathy の miniGPT 実装を Python で読む |
第23章 |
microgpt.py を Mojo で書き直す |
Tape・SoA・所有権を活かした Mojo 実装 |
第24〜28章 |
MAX での高速化 |
MAX Graph API で推論を最適化 |
第29〜30章 |
PyTorch・MLX 版 |
MPS・Unified Memory を活かした実装 |
第31章 |
まとめ |
全バリエーションの速度比較と Mojo の立ち位置 |
推奨ルート:
Python は使えるが Mojo は初めて → 第1〜4章を読み、第6、8、9、10章を順に進む
アセンブリやコンパイラに興味がある → 第5、7章を重点的に
すぐに ML の実装を動かしたい → 第16章から始め、詳細は必要に応じて参照