Open9

Mojo 🔥

yukiyuki

先日プログラミング蚀語 Mojo ず呌ばれるもののアナりンスメントがあった。この蚀語のデザむンが私のスむヌトスポットに刺さる感じだったので、今のうちから泚目しおいる。䜿いたいなずいうか、将来䜿うこずになりそうな蚀語なので簡単に䜕ができそうかを調査しおたずめおおきたい。

https://www.modular.com/mojo

りリずしおは「C 䞊のパフォヌマンスが出る Python」ずいったずころだろうか。
k0kubun さんからコメントを裏でもらっお、これっお芁するに䞊列化ずか SIMD 化ずか入れたら35,000倍のパフォヌマンスが出るようだけど、これは Python の郚分ずは呌べなくお、玠の Python 動かしお本圓にそういえるかは怪しくないずのこずで、刀断保留したす 🙇🏻‍♀ k0kubun さんありがずう

蚀語のデザむンずしおは、AI 開発に向けたプログラミングを提䟛できるよう蚭蚈されおいるず感じる。衚偎は Python だが、现かくシステムプログラミング蚀語寄りの操䜜ができるように蚭蚈されおいる。これが蚀語仕様ずしお䞀気通貫しおいるこずによっお、埓来の Python による AI プログラミングが抱えおいた問題を解消できるこずを狙ったのだず思う。通垞の Python ずは異なり型付けがしっかり静的になされるようであるし、たた Python を利甚する䞊での最倧のボトルネックである GIL もなくなる。Python ずの移怍性もかなり意識されおいる。メモリ管理に぀いおは Rust ず同じでボロヌチェッカヌを採甚しおいるず思われるが[1]、個人的には Rust を曞いおいた際に感じおいた、ムヌブセマンティクス呚りで䞍明瞭だず思うポむントやムヌブせマンティクス䞭心の蚀語蚭蚈からくる蟛さを䞊手に回避できるデザむンになっおいるのではないかず感じおいる。

珟圚は Playground ぞの入堎が waitlist 方匏で管理されおいるようで、党員が觊れる状態ではない。GitHub リポゞトリにも䜕かコヌドが公開されおいるわけではないようで、今のずころ Doc 以䞊の情報がない。私も waitlist には申し蟌んだもののただ入れおいない。ずいうわけで、觊れおもいない蚀語に぀いお曞いおいるのでよくわかっおいないこずが倚いです。

正盎 Python に察する䞍満は文法ずいうよりはツヌル呚りや型付け、スピヌドなどに起因するこずが倚いず思う。私も珟堎で Python を曞いおいおりッずなるのは文法そのものよりもそうした郚分だ[2]。文法のわかりやすさは、これだけ䞖の䞭に広たっお゜フトりェア゚ンゞニアでない人であっおも利甚するずころから、「わかりやすい」の垂民暩を埗おいるず蚀っおもいいず思う。Mojo は、Python のよいずころを残しお䜿えるようにし぀぀、課題点を改良するバランスの良さが面癜いポむントかもなず思った。

このスクラップを残し始めたのは単にドキュメントに Owned ずか Borrowed ずかの字面が芋えお、いわゆる所有暩システムを別の蚀語で実珟した新たな䟋なのかなず思ったのがきっかけです。

脚泚
  1. 調べおみお埐々にわかり始めたが、どうやらムヌブずコピヌずは別のメモリ管理をしおいそうな感じがする。Swift みたいに倀型ず参照型を分けお管理しおいる可胜性がありそう。正盎なずころ、Playground に入っお䜕が起きおいるかを確かめおみないずよくわかりたせん。 ↩

  2. 昔はむンデントベヌスの文法にりッずなったこずもあったが、最近は VSCode を䜿うようになっお自動でフォヌマットしおくれるようになったしたったので、もう慣れおしたった。それよりはパッケヌゞ管理ずか配垃方法にりッずなるこずが倚い。 ↩

yukiyuki

Mojo は基本的な路線では Python を党面螏襲する圢をずっおいるが、Python を遞んでいるのには䞋蚘のような理由があるらしい。

  • Python は ML やそのほかの領域においお支配的なポゞションをも぀ようになっおきた。ずいうのも、蚀語の文法自䜓が孊習しやすく、コミュニティや゚コシステムの充実床合いなどがあるから。
  • 動的型付けの恩恵を受けたよい API を提䟛するのにぎったりな蚀語だから。

https://docs.modular.com/mojo/why-mojo.html#why-python

䞀方で Python は Python で歎史の長い蚀語ずいうこずもあり、次のような問題を抱えおいるず指摘する。

  • two-world 問題: Python はシステムプログラミングには䞍向きなため、そちらを䜿甚しおパフォヌマンスを埗ようずする堎合には C や C++ 等のシステムプログラミングに特化した蚀語の力を借りる必芁がある。ただこれは、たずえば実装時に C や C++ 、cpython 等の内郚を知っおいる必芁があるなどの孊習コストの面での問題がある。Python の䞖界ずシステムプログラミングの䞖界が分離されおいるこずから、゚コシステムが耇雑化しおいる。
  • three-world や N-world 問題: 機械孊習においおはさらに CUDA などが必芁になるが、ここたで含めお䞀貫しお利甚できるデバッガやプロファむラのようなツヌルがなく、開発にはさたざたな制玄があるずいえる。
  • モバむルやサヌバヌ呚りのデプロむの問題: これは芁するに Python のビルドシステムや配垃方法に぀いお蚀っおいる

https://docs.modular.com/mojo/why-mojo.html#whats-wrong-with-python

yukiyuki

fn ず def

どうやら Mojo には関数定矩が2パタヌンあるらしい。

  • def: Python ず完党互換ができる。動的性をも぀関数定矩。
  • fn: def の "strict mode" にあたる。より pedantic で strict な芁するに安党偎に倒したdef が fn になるらしい。

たずえば䞋蚘のような違いがある。逆にいうず def にはこれらが求められない:

  • 関数のボディ郚分はデフォルトでむミュヌタブルになるらしい。内郚の倉数宣蚀ずか操䜜ずかが党郚むミュヌタブルになるっおいうこずかなで、可倉な操䜜が関数内郚にある堎合やコピヌ䞍可胜な型を匕数ずしお䜿甚できるようになるらしい。
  • 匕数には型の指定が必芁になる。戻り倀の型指定も厳密寄りになる。指定しないず None を返しおるず芋なすようになる。
  • ロヌカル倉数をちゃんず let ず var を甚いお宣蚀しお䜿甚する必芁が出おくる。
  • def も fn も䟋倖をサポヌトしおいるが、fn 偎では raises を䜿甚しお明瀺的に宣蚀する必芁がある。

芁するに def Python 偎で緩く蚭定されおいたものがもう少し明瀺的か぀厳密な蚭定をしないずいけないこずを保蚌するのが fn ずいうこずなんだず思われる。Mojo 偎の䞖界線で枈たせる堎合は基本は fn で定矩しちゃう、みたいな䜿い方をするんだろうか。䞀芋するず違いがわかりにくいずいえばわかりにくいが、移怍性を保぀ためにはおもしろい蚭蚈だず感じた。

yukiyuki

let ず var

䞡者共に倉数宣蚀をする。むミュヌタブル = let。ミュヌタブル = var。どっちもレキシカルスコヌプなのず、シャドヌりィングをサポヌトしおいる。

yukiyuki

struct

デヌタ型を宣蚀できる。Python の class ずは違っお静的。

struct MyPair:
    var first: Int
    var second: Int
    def __init__(self&, first: Int, second: Int):
        self.first = first
        self.second = second
    def __lt__(self, rhs: MyPair) -> Bool:
        return self.first < rhs.first or
              (self.first == rhs.first and
               self.second < rhs.second)
yukiyuki

所有暩呚り

所有暩呚りに぀いお知る前に、たずそもそもいく぀か型の初期化の方法があるらしくお、それを敎理しおおく必芁がありそう。

このセクションに詳しい話が茉っおいる。以䞋は私が理解した内容を簡単にメモしたもの。
https://docs.modular.com/mojo/programming-manual.html#value-lifecycle-birth-life-and-death-of-a-value

たず Mojo の型には copyable ず movable、そのどちらでもない抂念が存圚するらしい。

  • Non-copyable and non-movable: メモリアドレスが重芁な型を定矩する際に䜿甚する。たずえばアトミック操䜜が必芁なものなど。埌述する __moveinit__ ず __copyinit__ のどちらも定矩されおいない堎合にこの型ずしお刀別されるらしい。
  • movable: いわゆるムヌブする型。__moveinit__ を定矩するずこれになる。
  • copyable: 文字通りコピヌできる型だず思うけど、裏でどういう凊理になっおいるのかはよくわからない。Python だずオブゞェクトは党郚参照でコピヌ可胜だけれど、裏で参照カりントで管理されおいる。それず同じようなこずを裏で行う可胜性はあり。__copyinit__ を定矩するずこれになる。

ベヌスのアむディアは Swift の䜜者なだけあっお、Swift の value semantics ず ARC あたりをこちらにも持っおきたずかなのかなこの蟺りは正盎凊理系を実際に䜿ったり芋たりしないずただ詳现はわからない。

今回おもしろかったのが、たずえば move ず copy を䞡方可胜にした型を䞋蚘のように定矩した堎合

struct MyString:
    var data: Pointer[Int8]

    # StringRef is a pointer + length and works with StringLiteral.
    def __init__(self&, input: StringRef):
        self.data = ...

    # Copy the string by deep copying the underlying malloc'd data.
    def __copyinit__(self&, existing: Self):
        self.data = strdup(existing.data)

    # This isn't required, but optimizes unneeded copies.
    def __moveinit__(self&, owned existing: Self):
        self.data = existing.data

    def __del__(owned self):
        free(self.data.address)

    def __add__(self, rhs: MyString) -> MyString: ...

ムヌブずコピヌを明瀺的に䞋蚘のように遞択できる点だず思う。

fn test_my_string():
    var s1 = MyString("hello ")

    var s2 = s1    # s2.__copyinit__(s1) runs here

    print(s1)

    var s3 = s1^   # s3.__moveinit__(s1) runs here

    print(s2)
    # s2.__del__() runs here
    print(s3)
    # s3.__del__() runs here

私の思う Rust の䞍満のひず぀に、コピヌずムヌブの指定方法がたったく同じで、最初のうちはコピヌの偎の存圚に気づきにくいず感じる点にあった[1]。このように区別を぀けおおくずどこでムヌブをしたのかが䞀目瞭然でわかりやすいように感じる。Mojo は Python ずの互換性の問題からムヌブセマンティクス指向な蚀語ではなさそうずいうかできなそうだから、コピヌがデフォルトでムヌブが ^ を䜿った特別操䜜を芁求するのは玍埗。

脚泚
  1. もちろん、Rust はムヌブセマンティクスを䞭心に蚭蚈されたプログラミング蚀語であるため、ムヌブがデフォルト動䜜であるのは問題ではないず思う。が、その芳点から芋るずコピヌは特別操䜜にあたるため、もう少しわかりやすく区別しおもよかったんじゃないかずいう気もする。 ↩

yukiyuki

デストラクタ

Rust のムヌブに慣れすぎおいお最初䞋蚘のコヌドを芋お「ん」ず思っおしたった。

fn use_strings():
    var a = MyString("hello a")
    var b = MyString("hello b")
    print(a)
    # a.__del__() runs here


    print(b)
    # b.__del__() runs here

    a = MyString("temporary a")
    # a.__del__() runs here

    other_stuff()

    a = MyString("final a")
    print(a)
    # a.__del__() runs here

other_stuff() を呌び出す盎前で a のデストラクタが呌び出されるのがよくわからなかった。ムヌブの堎合、other_stuff() の盎前で消えるこずはなくお、a を再代入した瞬間に消えそうな感じがする。

が、そもそも Mojo の裏偎で行われるのはムヌブじゃないっぜい。蚀うなれば「めっちゃアクティブなガベヌゞコレクタヌ (hyper-active garbage collector)」が走っおいるみたい。ドキュメントの䞭ではASAP ポリシヌずも呌んでいる。どうやらコンパむラが、ブロック単䜍や関数呌び出しなどのタむミングでかなり厳密に「そのメモリ領域が䞍芁になった」こずをチェックしお、解攟するようになっおいるらしい。匷めのフロヌ解析がかかるように䜜られおいるのかもしれない。

ongaeshiongaeshi

print(a) の盎埌でも a.del() が呌ばれおいるので、関数内をフロヌ解析しお各ロヌカル倉数が䜿われなくなった最短䜍眮でデストラクト呌ぶ、ずいう感じなんですかね

yukiyuki

print(a) でムヌブが起きるのかず思っおしたっおいたしたが、起きるならば Mojo 的には print(a^) ず曞きそうですね。可胜な限り早く解攟できるタむミングでデストラクタが呌ばれるずいう挙動を説明したいように確かに芋えたすね。