6. 言語基礎(1)— 概要・関数・変数
6.1. この章で学ぶこと
Mojo の基本的な考え方
関数の書き方と読み方
変数とスコープの基本
この章では、Mojo Manual の Language 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、型推論、String、List をまとめて確認できます。
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(...)を混ぜて表示用の文字列を型をそろえて渡しています。
struct や trait の詳しい話は、後の章で扱います。
補足: 全体像をつかんだあとで、公式の 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
デフォルト引数
raiseとtry/except
pick_nonzero は、条件によってエラーを投げる関数です。
そのため、呼び出し側では try/except で受ける流れになります。
詳細: 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
補足: Python に慣れていると、
varのブロックスコープは引っかかりやすいです。ここは早めに意識しておくと、後で読みやすくなります。
6.5. この章を一文で言うと
Mojo の基本は Python に似て見えるが、型・スコープ・所有権をよりはっきり意識して書く言語だ、ということです。
6.6. まとめ
Mojo は Python に似た見た目を持つが、静的型の言語である
関数では、引数や戻り値、
raisesなどのシグネチャが大事になる()と[]では役割が違う変数では、
varと暗黙宣言でスコープが異なる^が出てきたら、所有権の移動を意識する
次の章でも、同じように 「Python に似ている部分」と「Mojo 独自の考え方」 を切り分けながら読むと理解しやすいです。