0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Mojo🔥コンパイラのオープンソース化を機に、Python経験者が1時間で体感。CLIサンプルコード付き

0
Last updated at Posted at 2026-08-31

はじめに

2026 年 8 月 18 日、Modular 社がプログラミング言語「Mojo🔥」のコンパイラをオープンソース化しました。

これまで Mojo は、標準ライブラリは 2024 年からオープンソースだったものの、肝心のコンパイラ本体は非公開のままでした。今回、Apache 2.0 ライセンス(LLVM 例外付き)のもとでコンパイラ・ツーリングまで含めて公開され、GitHub のmodularリポジトリで誰でもソースを読めるようになっています。

ちょうど今月、今後期待できそうな言語として Mojo が話題になっていたので、実際に触ってみることにしました。この記事は、Python 経験者が Mojo の環境構築から基礎文法、簡単な CLI アプリの実装までを一通り体験する記事です。

1. 環境を作る

Pixi のインストール

Mojo の公式インストール方法は uv と Pixi の両方が案内されています。今回は Pixi を使います。Pixi は Rust の Cargo や npm に近い、プロジェクト単位で依存関係と実行環境を管理するツールで、conda のエコシステム(conda-forge)をベースに、Python・Rust・Node.js など多言語のパッケージを扱えます。

$ curl -fsSL https://pixi.sh/install.sh | sh

プロジェクト作成と Mojo のインストール

$ pixi init life -c https://conda.modular.com/max/ -c conda-forge && cd life
$ pixi add mojo
$ pixi run mojo --version
Mojo 1.0.0 (ed45d567)

バージョン 1.0.0 が表示されました。Mojo 1.0 のリリースは 2026 年 8 月 11 日で、コンパイラの OSS 化(8 月 18 日)はちょうどその 1 週間後です。

VSCode 拡張機能

シンタックスハイライトや LSP、デバッガまで揃っている公式拡張機能があるので、入れておくと快適です。

2. なぜ速いのか: インタプリタとコンパイラの違い

Mojo のコードは見た目が Python そっくりです。

def main():
    print("Hello, World!")

hello.mojoとして保存し、ビルドして実行してみます。

$ mojo build hello.mojo
$ ./hello
Hello, World!

ただし中身の実行のされ方は Python と全く違います。どちらもまずソースコードを解析しますが、大きく異なるのはその後の実行方法です。

  • インタプリタ(CPython): ソースコードの意味を解析しながら、その場で動作を実行する。CPU の機械語ではなく Python VM 向けの中間言語(バイトコード)に変換したうえで、VM が 1 命令ずつ解釈しながら動かす
  • コンパイラ(Mojo): ソースコードを、CPU が直接実行できる機械語に丸ごと変換してから実行する。VM のような仲介者を挟まない

ちなみに Mojo のコンパイラは、LLVM プロジェクトのコンパイラ基盤「MLIR」をベースに構築されています。ソースコードは AST を経由せず直接 MLIR の中間表現になり、複数段階の変換を経て LLVM IR、そして機械語になります。Mojo の開発者 Chris Lattner 氏は LLVM の生みの親であり、MLIR の立ち上げにも関わった人物です。

Chris Lattner at FOSDEM 2011
Chris Lattner, FOSDEM 2011(colorized by Waldyrious, CC BY-SA 4.0, via Wikimedia Commons)

3. 実測: 円周率の計算で速度を比較する

理屈だけでなく、実際にどれくらい速いのか測ってみます。ライプニッツの公式(円周率 = 4 × (1 - 1/3 + 1/5 - 1/7 + ...))を使って、同じロジックを Python と Mojo で書き、limitを 10 倍ずつ増やしながら実行時間を比較しました。比較に使ったバージョンは Python 3.11.9、Mojo 1.0.0 です(実行環境: Apple M4 Pro / macOS)。

import time

print("開始")
limit = 10
for _ in range(0, 9):
    begin = time.perf_counter()
    n = 1
    l = 0.0
    while n < limit:
        l += (1.0 / n) - (1.0 / (n + 2))
        n += 4
    print(l * 4, time.perf_counter() - begin, "")
    limit *= 10
print("終了")
from std.time import perf_counter

def main():
    print("開始")
    var limit: Int = 10

    for _ in range(0, 9):
        var begin = perf_counter()
        var n: Int = 1
        var l: Float64 = 0.0

        while n < limit:
            l += (1.0 / Float64(n)) - (1.0 / Float64(n + 2))
            n += 4

        print(l * 4, perf_counter() - begin, "")
        limit *= 10

    print("終了")
$ mojo build pi.mojo
$ ./pi

limitごとの実行時間を比較すると、以下のようになりました(円周率の近似値は両言語で完全に一致しています)。ブレを考慮して 3 回計測した中央値を載せています。

limit Python Mojo 倍率
10^7 0.2807 秒 0.0018 秒 約 157 倍
10^8 2.4497 秒 0.0180 秒 約 136 倍
10^9 24.7299 秒 0.1933 秒 約 128 倍

limit = 10^9では約 128 倍、Mojo の方が速い結果になりました。

これは言語一般の性能差を示すものではなく、あくまで今回の数値計算ループを手元の環境で測定した結果です。

4. 基礎文法をおさえる

CLI アプリを作る前に、Python と Mojo で挙動が違う部分を中心に、基礎文法を確認します。

変数: varcomptime

Mojo の変数宣言はvarを使います。型注釈は:の後ろ、値は=の後ろという位置のルールがあります(TypeScript と同じ並びです)。

var num_rows = 8          # 型は自動推論
var num_cols: Int = 8     # 型を明示

Python と違い、関数の外(グローバルスコープ)に書き換え可能なvarを置くことはできません。

var count = 0  # ファイル直下に書くとエラー
global variables are not supported; move this into a function body or use 'comptime' to declare a constant

コンパイル時に値を決定できる定数であれば、comptimeでグローバルに置けます。

comptime GREETING: String = "Hello"

comptimeは「コンパイル時に値が確定していないといけない」という制約があります。

エラー処理: raisestry/except

例外を投げる可能性がある関数には、シグネチャにraisesを明示する必要があります。

def calculate_average(temps: List[Float64]) raises -> Float64:
    if len(temps) == 0:
        raise Error("気温データがありません")
    var total = 0.0
    for temp in temps:
        total += temp
    return total / Float64(len(temps))

呼び出す側はtry/exceptで受け止めるか、自分もraisesを付けて上位に伝播させる必要があります。

def main() raises:
    try:
        var avg = calculate_average(temps)
        print("平均気温:", avg)
    except e:
        print("エラー:", e)

raisesが無い関数の中でraiseしようとすると、次のようなエラーになります。

'raise' requires a surrounding 'try' block or the enclosing function to declare 'raises'

構造体: struct

Mojo には(2026 年 8 月現在)Python のclassに相当するものがまだありません。将来的にサポート予定とのことですが、今は代わりにstructを使います。

@fieldwise_init 
struct Person(Copyable):
    var name: String
    var age: Int

def main():
    var mine = Person("Taro", 30)
    print(mine.name, mine.age)

@fieldwise_initは、フィールドをそのまま受け取るだけの__init__を自動生成してくれるデコレータです。単純な代入以上のこと(デフォルト値を用意する、値を検証するなど)をしたい場合は、自分で__init__を書きます。

Listは暗黙にコピーされない

Listのような型は、デフォルトでは暗黙的にコピーできません。

var first_list: List[Int] = [1, 2, 3]
var second_list: List[Int] = first_list  # エラー
value of type 'List[Int]' cannot be implicitly copied, it does not conform to 'ImplicitlyCopyable'

コピーしたい場合は.copy()を明示的に呼びます。

var second_list: List[Int] = first_list.copy()

5. 在庫管理 CLI を作ってみる

ここまでの内容を踏まえて、struct を実際に使ったサンプルコードを書いてみます。商品(Item)を struct で表現し、一覧・入庫・削除ができる在庫管理ツールを作りました。

def read_item_index(inventory: Inventory) raises -> Int:
    for i in range(len(inventory.items)):
        print(i + 1, ":", inventory.items[i].name)
    var choice_str = input("")
    var choice: Int
    try:
        choice = Int(choice_str)
    except e:
        raise Error("数字を入力してください")
    if choice < 1 or choice > len(inventory.items):
        raise Error("存在しない番号です")
    return choice - 1

@fieldwise_init
struct Item(Copyable):
    var name: String
    var stock: Int

struct Inventory(Copyable):
    var items: List[Item]

    # 引数なしで空の在庫を作りたいので、@fieldwise_initではなく手書き
    def __init__(out self):
        self.items = List[Item]()

    def add_item(mut self, item: Item):
        self.items.append(item.copy())

    def list_items(self):
        for item in self.items:
            print(item.name, item.stock)

    def add_stock(mut self, index: Int, qty: Int):
        self.items[index].stock += qty

    def remove_item(mut self, index: Int) raises -> String:
        var name = self.items[index].name
        _ = self.items.pop(index)
        return name

def main() raises:
    var inventory = Inventory()
    inventory.add_item(Item("りんご", 50))
    inventory.add_item(Item("ぶどう", 5))

    while True:
        print("--------------------")
        print("1: 一覧  2: 入庫  3: 削除  4: 終了")
        var choice_str = input("")

        if choice_str == "4":
            print("終了します")
            break
        elif choice_str == "1":
            inventory.list_items()
        elif choice_str == "2":
            try:
                print("どの商品に入庫しますか?")
                var index = read_item_index(inventory)
                print("何個入庫しますか?")
                var qty_str = input("")
                var qty: Int
                try:
                    qty = Int(qty_str)
                except e:
                    raise Error("数字を入力してください")
                inventory.add_stock(index, qty)
                print(inventory.items[index].name, "", qty, "個入庫しました")
            except e:
                print("エラー:", e)
        elif choice_str == "3":
            try:
                print("どのアイテムを削除しますか?")
                var index = read_item_index(inventory)
                var name = inventory.remove_item(index)
                print(name, "を削除しました")
            except e:
                print("エラー:", e)
        else:
            print("1〜4の番号を入力してください")

設計のポイントがあります。ライブラリ由来のエラーを自分の言葉に翻訳する、という点です。Int()での変換に失敗すると、Mojo 標準ライブラリが生成する次のようなメッセージがそのまま出ます。

String is not convertible to integer with base 10: 'ぶどう'

これは開発者向けの技術的なメッセージなので、try/exceptで一度捕まえて「数字を入力してください」という利用者向けのメッセージに詰め替えています。

実行結果

$ mojo build item.mojo
$ ./item
--------------------
1: 一覧  2: 入庫  3: 削除  4: 終了
→ 1
りんご 50
ぶどう 5
--------------------
1: 一覧  2: 入庫  3: 削除  4: 終了
→ 2
どの商品に入庫しますか?
1 : りんご
2 : ぶどう
→ 1
何個入庫しますか?
→ 10
りんご に 10 個入庫しました
--------------------
1: 一覧  2: 入庫  3: 削除  4: 終了
→ 4
終了します

一覧・入庫・削除がひと通り動く、簡単な在庫管理ツールができました。

おわりに

コンパイラのオープンソース化というタイミングで、Python 経験者の目線で Mojo の基礎を一通り触ってみました。

classはまだ無く、Web 向けのエコシステムも発展途上です。現状は AI/ML のホットパスや、GPU カーネルの高速化が本命のユースケースなのかもしれません。

オープンソース化されたので、今後の動向を追いかけていきたいと思います。

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?