Pythonの35000倍速い新言語Mojo🔥 に触れてみた【基礎編】

# Mojo🔥とは
PythonのシンプルさとRustのスピードとメモリの安全性を組み合わせた、新しいプログラミング言語。

https://www.modular.com/mojo

Pythonの35000倍速いとされており、Pythonの弱点である「遅さ」を補うと期待されている。また、書き方もPythonと似ているので移行もしやすいです。

そんな言語が2023年10月19日より、Appleシリコン搭載Macに対応したとのことで、早速触ってみました。

https://docs.modular.com/mojo/

本家のドキュメントはここにまとまっています。

## Mojo🔥のセットアップ
ターミナルやVSCodeで使えるようにセットアップが必要です。

以前記事にまとめたので、そちらをご覧ください

https://qiita.com/mohki7/items/b09d5c3d4a10e333d060

# Mojo🔥の基本
主にPythonとの比較をしながら書いていきます。

## 前提
・Mojo🔥はコンパイル言語であり、他のコンパイル言語たち同様、エントリーポイントとして ```main()``` が必要
・Pythonのすべての構文とセマンティクスをサポート（Pythonの書き方でも実行可能）

## 変数 var, let
```mojo
var x: Int = 1
let y: Int = 2
```
varは可変な変数、letは不変な変数の定義に用います。Intなどの型タイプは省略も可能です。

## 関数 : def → fn

Python:
```Python
def main():
    x = 1
    x += 1
    print(x)
```
Mojo🔥
```mojo
fn main():
    var x: Int = 1
    x += 1
    print(x)
```
ただ、defのままでも実行できます。fnにすると型の安全性を強制するようにできます。

なので、fnの中でvarなどを削除するとエラーになります。

また、関数に引数や返り値を設定する場合は、型タイプを明記する必要があります。書かないとエラーが出ます

・OKな例

```mojo
fn add(x: Int, y: Int = 1) -> Int:
    return x + y
```
・ダメな例
```mojo
fn add(x, y):
    return x + y
```
引数のデフォルト値の設定方法はPythonと同じです

### borrowed と inout と owned
関数の引数はimmutableな変数（変更不可）として読み取られます。
従って、以下はエラーになります

```mojo
fn add(x: Int, y: Int) -> Int:
    x += 1
    return x + y
```
<b>borrowed</b>はそのことを明記する（保証する）役割らしいです
→関数がそのオブジェクトの所有権を持たず変更しないことを保証している
※borrowedは、特に何も書いていない上の```fn add()``` と同じ挙動になります。
→デフォルトでborrowedが省略されていると考えれば良い？

```mojo
fn add(borrowed x: Int, borrowed y: Int) -> Int:
    return x + y
```
さらに、<b>inout</b>というものがあります。
```mojo
fn add(inout x: Int, inout y: Int) -> Int:
    return x + y
```
inoutとして設定すると、引数をmutableな変数（変更可能）とできます。そして関数内部で加えられた変更は外部にも影響します。
```
fn main():
    var a = 1
    var b = 2
    let c = add_inout(a, b)
    print(a)
    print(b)
    print(c)


fn add_inout(inout x: Int, inout y: Int) -> Int:
    x += 1
    y += 1
    return x + y
```
これを実行すると、
```
2
3
5
```
となります。aは元々1, bは2でしたが、```fn add_inout()``` の中で1ずつ足されたので、それぞれ2, 3になっています。

<b>owned</b>は、引数をmutable（変更可能）とするものの、関数の外には影響を与えないとするものです。
先ほどのコードでinoutをownedに変更すると、
```
fn main():
    var a = 1
    var b = 2
    let c = add_inout(a, b)
    print(a)
    print(b)
    print(c)


fn add_inout(owned x: Int, owned y: Int) -> Int:
    x += 1
    y += 1
    return x + y
```
結果は
```
1
2
5
```
となって、aもbも変更されていないことがわかります。

## クラス class → struct
完全に静的なclassのようなもので、メソッド、フィールド、オーバーロード、デコレータなどを定義できます。

```mojo
struct MyPair:
    var first: Int
    var second: Int

    fn __init__(inout self, first: Int, second: Int):
        self.first = first
        self.second = second

    fn dump(self):
        print(self.first, self.second)

fn main():
    let mine = MyPair(2, 4)
    mine.dump()
```
第一引数に```self``` を渡すところや```__init__()``` を定義するところはPythonのclassと同じです

出力は
```
2 4
```
となります。Pythonのclassと違うところとして、```__init__()``` で定義する```self.first``` や```self.second``` は事前に```var first: Int``` と定義しておく必要があることが挙げられます。これがないとエラーになってしまいます。

```
error: 'MyPair' value has no attribute 'first'
        self.first = first + 1
```
また、```__init__()``` の引数は```inout```として渡します。

これは、イニシャライザはオブジェクトの初期化を担当する部分であり、このメソッド内で```self```オブジェクトを変更するので引数はmutableでなければなりません。したがって```borrowed```は適しません。

さらに、```self```オブジェクトを変更してこのメソッド外においてもその変更を適用させたいので```owned```も適しません。したがって、```inout```とする必要があります。

まあ、一言で言えば、「selfはインスタンスを示すので、__init__で行われるインスタンスの初期化をクラス内の他のメソッドでも適用させたいため、inoutとする」ということです。

## Pythonライブラリを使う Python integration
ドキュメント通りのコードを実行するとエラーが出る。。。

numpyをうまく読み込めないっぽい。色々調べたけど治らず。
まだ情報が少ないからこういう問題が発生した時の対処が大変です。。



# 最後に
まだまだMojo🔥は発展途中の言語で、ドキュメントにもそのことは繰り返し書かれていました。Pythonに似ていて書きやすいので今後の発展が楽しみです。

次はPythonとの速さの比較の記事を書こうと思っているので、読んでもらえたら嬉しいです！

また、X(Twitter)では日々データサイエンスやAIに関する勉強記録・情報発信をしています。フォロワーはもうすぐ5500人に到達します。

https://twitter.com/A7_data

少しでも刺激になるようなことを発信できたらいいなと思っているので、気になった方は見てみてください🔥

では👋