6. 言語基礎(1)— 概要・関数・変数

6.1. この章で学ぶこと

  • Mojo の基本的な考え方

  • 関数の書き方と読み方

  • 変数とスコープの基本

この章では、Mojo ManualLanguage basics にあたる内容のうち、全体像・関数・変数を整理します。

公式の同じ見出しを開きながら読むと、流れを追いやすいです。

本章のコード例は Mojo 0.26 系 で確認しています。 def ... raises -> T が使える版です。

0.24 系など古い版 では、raises の扱いが異なることがあります。 その場合は次を確認してください。

6.2. Language basics

まず大事なのは、Mojo は Python に似た書き方をしつつ、別の言語として設計されていることです。

ポイントは次の通りです。

  • 構文は Python に近い

  • ただし 静的型 で動く

  • メモリ安全 を重視している

  • CPU や GPU を意識した、次世代コンパイラ基盤の上で使われる

ここで大事なのは、見た目が Python でも、中身の考え方はかなり違うことです。

6.2.1. 最初に押さえること

  • エントリポイントは def main()

  • ブロックは : とインデントで表す

  • 変数は var または暗黙宣言で作る

  • いったん決まった型は、あとで別の型に変えられない

  • 関数は def で定義する

  • 型やメソッドのまとまりは struct で表す

  • Python モジュールを使う入口もある

たとえば、NumPy などの Python 資産を使えるのは Mojo の大きな特徴です。 ただし、この章では入口だけ押さえれば十分です。

6.2.2. 最小の例

次の例では、エントリポイント、型注釈付きの var、型推論、StringList をまとめて確認できます。

def main():
    var n: Int = 42
    var doubled = n * 2
    var xs: List[Int] = [1, 2, 3]
    var label = String("count")
    print(String("n="), n, String(" sum="), doubled, String(" "), label, String("="), len(xs))

リスト-1: language_basics_minimal.mojo

6.2.3. コードの読み方

  • xs: List[Int] を型注釈つきで宣言し、リストリテラルで初期化する書き方です。

  • doubled: 右辺から型が決まる暗黙宣言で、一度決まった型は変えられません。

  • print: String(...) を混ぜて表示用の文字列を型をそろえて渡しています。

structtrait の詳しい話は、後の章で扱います。

詳細: Mojo language basics

出典: Mojo Manual — basics

補足: 全体像をつかんだあとで、公式の Get started を一度通すと理解しやすいです。

6.3. Functions

この章での要点は、Mojo の関数は Python に似て見えても、型とシグネチャをかなり大事にすることです。

6.3.1. 基本

  • 関数は def で定義する

  • () は実行時に渡す引数

  • [] はコンパイル時に決まる parameter

  • 戻り値は -> で書く

  • 同じ名前でも、引数型が違えば別の関数として定義できる

6.3.2. まず覚えたいこと

6.3.2.1. def を使う

現在の Mojo では、関数定義は def が基本です。 fn は非推奨になっています。(以前は fn で定義していました)

6.3.2.2. ()[] は役割が違う

  • () は実行時の値を渡す

  • [] はコンパイル時に決まる値を渡す

たとえば repeat[count] のような形では、count は実行中に変わる値ではありません。 コンパイル時に決まる値として扱われます。

つまり、コンパイラはその値に合わせて特化したコードを作れます。

6.3.2.3. オーバーロードがある

同じ関数名でも、引数の型が違えば別の関数として定義できます。 これがオーバーロードです。

6.3.2.4. デフォルト引数も使える

引数を省略したときだけ、既定値が使われます。 この感覚は Python にかなり近いです。

6.3.2.5. raises が必要なことがある

エラーを投げる関数では、この関数はエラーを外に出す可能性があるとシグネチャに書きます。 それが raises です。

たとえば次のような形です。

  • def pick_nonzero(...) raises -> Int:

ここで大事なのは、エラー処理も関数の型情報の一部として見ることです。 この点は Python より厳密です。

6.3.3.

def show(a: Int):
    print("int", a)


def show(s: String):
    print("str", s)


def repeat[count: Int](msg: String):
    for _ in range(count):
        print(msg)


def greet(name: String = "world"):
    print(String("Hello, "), name)


def pick_nonzero(x: Int) raises -> Int:
    if x == 0:
        raise Error("zero not allowed")
    return x


def main():
    show(1)
    show(String("hi"))
    repeat[2](String("Hello"))
    greet()
    greet(String("Mojo"))
    try:
        _ = pick_nonzero(0)
    except e:
        print("caught:", e)

リスト-3: functions_overload_params.mojo

この例では、上から順に次を確認できます。

  • オーバーロード

  • compile-time parameter

  • デフォルト引数

  • raisetry/except

pick_nonzero は、条件によってエラーを投げる関数です。 そのため、呼び出し側では try/except で受ける流れになります。

詳細: Functions

出典: Mojo Manual — functions

補足: out 引数や、オーバーロード解決の細かい規則は、必要になった時点で公式を読むので十分です。

6.4. Variables

この章で特に大事なのは、Mojo では変数の型とスコープをかなりはっきり意識することです。

6.4.1. 基本

  • 変数は var で宣言できる

  • 右辺から型を決める 暗黙宣言 もある

  • ただし型は途中で変えられない

  • スコープの違いを理解することが大切

6.4.2. var と暗黙宣言の違い

6.4.2.1. var はブロックスコープ

var で作った変数は、そのブロックの中だけで有効です。 たとえば if の中で作った変数は、外では使えません。

def block_var():
    if True:
        var x = 1
        print("inside", x)
    # ここでは x は使えない(ブロックスコープ)


def main():
    block_var()

リスト-4: variables_block_var.mojo

6.4.2.2. 暗黙宣言は関数スコープ

一方で、暗黙宣言で導入した名前は、関数全体で見える形になります。 Python に近く感じるのはこの部分です。

def implicit_scope():
    y = 10
    if True:
        y = 20
    print("y after if:", y)


def main():
    implicit_scope()

リスト-5: variables_implicit_scope.mojo

if の内側で y に再代入しても、その y は関数の後ろでも見えます。 var か暗黙宣言かでスコープの規則が変わると押さえておくとよいです。

6.4.3. 所有権の移動

Mojo では、値を渡すときに 所有権 を意識する場面があります。

^ は、この値を相手に渡し切ることを表す記号です。

たとえば msg^ を渡すと、そのあと元の msg はそのまま読めなくなります。 これは、値が相手側へ移ったと捉えるとよいでしょう。

受け取る側は、現在の書き方では次のように書きます。

  • def take(var s: String):

ただし古い版では書き方が違うことがあります。 0.24 系では、owned を使う形が見られます。

def take(var s: String):
    print(s)


def main():
    var msg = String("hi")
    take(msg^)

リスト-6: variables_ownership_transfer.mojo

ref やコピー規則の細かい話は、10 章値・所有権・ライフサイクル)で詳しく見れば十分です。

詳細: Variables

出典: Mojo Manual — variables

補足: Python に慣れていると、var のブロックスコープは引っかかりやすいです。ここは早めに意識しておくと、後で読みやすくなります。

6.5. この章を一文で言うと

Mojo の基本は Python に似て見えるが、型・スコープ・所有権をよりはっきり意識して書く言語だ、ということです。

6.6. まとめ

  • Mojo は Python に似た見た目を持つが、静的型の言語である

  • 関数では、引数や戻り値、raises などのシグネチャが大事になる

  • ()[] では役割が違う

  • 変数では、var と暗黙宣言でスコープが異なる

  • ^ が出てきたら、所有権の移動を意識する

次の章でも、同じように 「Python に似ている部分」と「Mojo 独自の考え方」 を切り分けながら読むと理解しやすいです。