(Pythonista 向けの読み替えとコード例)= # Pythonista 向けの読み替えとコード例 ## この章で学ぶこと - Mojo の位置づけの再確認 - Pythonista がつまずきやすい点 - ownership・compile-time・trait の読み替え この章は、Python 経験者向けの補足です。 コードを見ながら、**Mojo が Python とどこまで似ていて、どこから違うのか** を整理します。 まずは次の3つを押さえると読みやすいです。 - 最初の見た目はかなり Python に近い - でも中身はコンパイル言語寄り - ownership と compile-time がわかれ目になる ## Python との違いの地図 この章では、Python 経験者がつまずきやすいポイントを、コード例を通して確認します。 | Python の感覚 | Mojo での読み替え | |---------------|------------------| | 名前がオブジェクトを指す | 誰がその値を所有しているか | | 関数は何でも受け取れる | `read` / `mut` / `var` でシグネチャに意図を出す | | 例外は実行時に伝わる | `raises` でシグネチャに明示する | | 型ヒントは補助的 | `[]` はコンパイル時情報・コード生成に直接効く | | duck typing | trait で必要な能力を宣言する | 以降は、この読み替え表の各行をコードで確認していきます。 ## ownership と value semantics Mojo を学ぶときの大きなわかれ目がここです。 ### 基本 - 値には owner がある - owner の寿命が終わると値は破棄される - 参照はそのあいだだけ安全に使える - GC なしで安全性を目指す ```mojo ref value_ref = list[0] ``` Python では、変数は「オブジェクトへの名前」と考えることが多いです。 一方で Mojo では、**誰がその値を持っているか** をより強く意識します。 つまり、 > 「名前がオブジェクトを指す」というより、 > **「誰がその値を持っているか」** を意識する世界 と考えると入りやすいです。 出典: [Ownership](https://docs.modular.com/mojo/manual/values/ownership/) > **補足:** 最初は少し重い考え方ですが、安全性と性能を両立するための土台です。 ## `read` / `mut`: 関数シグネチャが意味を持つ ```mojo def add(mut x: Int, read y: Int): x += y def main(): var a = 1 var b = 2 add(a, b) print(a) # 3 ``` この例では、引数の役割がシグネチャに出ています。 - `read` は読み取り用 - `mut` は書き換え用 ここで大事なのは、**その関数が何をするかを、引数宣言の時点でかなり読める** ことです。 Python では、破壊的かどうかを名前や説明に頼ることが多いです。 Mojo では、それをシグネチャに出しやすいです。 出典: [Ownership](https://docs.modular.com/mojo/manual/values/ownership/) > **補足:** 「Python よりシグネチャが多くを語る言語」と理解するとよいでしょう。 ## `mut` の実務的な意味: コピーしない更新 ```mojo def mutate(mut l: List[Int]) raises: l.append(5) def main() raises: var values = [1, 2, 3, 4] mutate(values) print(values) # [1, 2, 3, 4, 5] ``` この例では、`mut` によって **破壊的に更新する** ことが明示されています。 ポイントは次の通りです。 - 更新することがわかりやすい - 不要なコピーを避けやすい - 意図が API に出る これは地味ですが、実務ではかなり重要です。 出典: [Ownership](https://docs.modular.com/mojo/manual/values/ownership/) > **補足:** 性能が大事なコードほど、コピー回数や aliasing の制御が効いてきます。 ## `var` と `^`: ownership transfer ```mojo def take_text(var text: String): text += "!" print(text) def main(): var message = "Hello" take_text(message^) # print(message) # ここでは使えない ``` ここでは、所有権の移動がはっきり出ています。 - `var` は受け取る側が ownership を持つ - `^` は呼び出し側から ownership を渡す - 渡したあとの元の変数は使えない これは Python にはあまりない感覚です。 ここで大事なのは、**この値はもう自分では使わない、と明示できる** ことです。 そのぶん、コンパイラは危険な使い方を防ぎやすくなります。 出典: [Ownership](https://docs.modular.com/mojo/manual/values/ownership/) > **補足:** システムプログラミング言語に求められる安全性の中核にある考え方です。 ## compile-time parameters ```mojo def repeat[count: Int](msg: String): comptime for i in range(count): print(msg) def main(): repeat[3]("Hello") ``` 出力: ```text Hello Hello Hello ``` この例で大事なのは、`[]` が実行時引数ではないことです。 - `[]` は compile-time input - `()` は runtime input - `comptime for` でコンパイル時に展開できる つまり、**見た目は関数呼び出しでも、コード生成に直接効く情報を渡している** ということです。 出典: [Parameters](https://docs.modular.com/mojo/manual/parameters/) > **補足:** Python の型ヒントよりも、性能戦略に近い仕組みです。 ## type generic + value generic ```mojo def repeat[ MsgType: Writable, count: Int ](msg: MsgType): comptime for _ in range(count): print(msg) def main() raises: repeat[2](42) repeat[3]("mojo") ``` この例では、2種類の parameter が出ています。 - `MsgType` は型 parameter - `count` は値 parameter どちらも compile-time specialization に効きます。 ここで大事なのは、**型だけでなく値までコンパイル時に持ち込める** ことです。 これが、固定長配列や SIMD、GPU 寄りの処理と相性がよい理由のひとつです。 出典: [Parameters](https://docs.modular.com/mojo/manual/parameters/) / [Generics](https://docs.modular.com/mojo/manual/generics/) > **補足:** ここでも、Mojo が typed Python ではなく compile-time 指向の言語だと見えてきます。 ## traits / generics ```mojo def all_equal[ T: Equatable & Copyable ](ref lhs: List[T], ref rhs: List[T]) -> Bool: if len(lhs) != len(rhs): return False for left, right in zip(lhs, rhs): if left != right: return False return True ``` ```mojo print(all_equal([1, 2, 3], [1, 2, 3])) print(all_equal(["hello", "world"], ["hello", "world"])) ``` この例では、trait 制約が **その型に必要な能力** を表しています。 - `Equatable` は比較できること - `Copyable` はコピーできること つまり、**T は何でもよいわけではなく、必要な能力を持っていなければならない** ということです。 ここで大事なのは、generic が型安全のためだけでなく、 **具体的なコード生成にも関わる** ことです。 出典: [Generics](https://docs.modular.com/mojo/manual/generics/) > **補足:** Python の duck typing よりも、必要な能力を先に言語側で宣言する感覚に近いです。 ## この章を一文で言うと **Mojo は最初は Python に近く見えるが、本質は ownership・compile-time・trait を土台にした別の言語です。** ## まとめ:読み替えのポイント | 仕組み | 対応する Python の感覚 | Mojo での違い | |--------|----------------------|--------------| | `read` / `mut` | 引数の in/out 区別 | シグネチャで明示 | | `var` / `^` | 代入・コピー | 所有権の移動 | | `[]` parameter | ジェネリクス・型ヒント | コンパイル時入力・特化 | | `trait` | Protocol / ABC | コード生成にも効く | | `raises` | 例外のある関数 | シグネチャで型として明示 | **「似ているのは文法であって、値の考え方やコンパイル時の仕組みはかなり違う」** ― この一点を押さえると、以降のコードが読みやすくなります。