Odin言語を始めて数日(まだ2, 3日くらい)だけど、OSCのライブラリが(OSC Bundleまで扱えて送受信もできるやつが)なさそうだったので、作った。

https://github.com/funatsufumiya/odin-osc

これを作ってみて、改めてOdinはすごく良い言語だというのを実感した。

メモリリークとの戦い

手動メモリ管理については、ぶっちゃけ予想通りの惨敗 笑。デバッグする前は自信満々に書いて、実際にいくつもテストケースを書いていくと、リークしてますよ、二重freeしてますよのオンパレード 笑。

でもすごいなと思ったのが、テストケースではデフォルトでTracking Allocatorがオンになって、リーク等していると警告してくれるので助かる。そうやってテストケースを整備すると、それがexampleとなっていく。

実際手動メモリ管理が難しいなと感じたのは、mapや配列などわかりやすい構造体ではなくて、構造体のなかで複雑にメモリアロケーションしている場合などは、リークしている箇所はすぐわかるものの、実際どうやって解放すればいいのか若干よくわからなくなったりする。

例えば、配列の中身がヒープにあるときに、配列の中身はdeleteしたのにその配列自体をdeleteし忘れたりなどなど。どうしてもわからなくなってGitHub Copilotに聞いたらアハ体験することもしばしば。

自分はこの辺のノウハウがまだ本当に初心者なので、今から慣れていくのだろうと思う。ただ、Tracking Allocator のおかげで、今のところメモリリークなくライブラリを書けているのは素晴らしい。

オーバーロードと明示的関数

Zigの場合は deinit などの共通の名前でデストラクタを書く文化があるのだけど、Odinの場合はメソッドがないし、いわゆる関数オーバロードできるのは明示的オーバーロードに限られるので、delete系は正直再定義できなくて、名前が乱立する。

こればかりはもうC言語/Go言語の宿命というか、明示的にすべてがわかるのが良いという哲学なので、仕方がないと割り切るしかない。他のライブラリが実際どうしているかは今はわからないけれど、コアライブラリが使っている名前付けルールを尊重しながら作ったりして誤魔化す。

ただオブジェクト指向で名前があまり衝突しない文化に慣れていると、なかなか慣れないのも事実。V言語でもそうだったけど、特にV言語はデフォルト引数がとれなかったりしてオーバーロードに関してはもっともっと厳しいので、Go言語ライクなものを書いていくときはこればかりは慣れないといけないのかなと思う。

(ただ、ライブラリユーザがどうやって構造体を delete すればいいのかわからなくなったりしないのだろうか…?その辺はユーザコミュニティ等に今後潜っていって慣習や感触を知りたい部分。1

Unionとバリアント (Rustのenum)

下記動画でも語られているけれど、Odinのunionは素晴らしいというのは前評判で聞いていた。

https://www.youtube.com/watch?v=bGc7C3U89-I

Rustのようなパターンマッチこそないものの、関数型のバリアントとほぼ同じ機能があり、Rustのenumとほぼ同等なので、C言語では正直メモリが厳しいときだけ使うように形骸化しているunionを、バリアントとしてきちんと使えるようにしてあるのはすごい。

そして、distinct typeの存在のおかげで、例えば同じ数値でも通貨型みたいなものを作ったりしても、バリアントで透過的に扱えるので、型が違うからと悩むことはあまりない。

switch caseが、バリアントのときだけ switch _ in value となるのだけは慣れないけれど、これも慣れかなとは思う。VSCode上でのLSP (ols)が、バリアントの型の名前空間を間違ったりすることがあるけど、これも愛嬌かな。LSPの完成度は基本的に非常に高く、この程度の問題で済んでいるのはすごい。

まとめ

全体的に、すごくOdinは理想的な言語だと思った。もしGo言語系が好きで、慣れている人なら、きっとOdinもすぐ使えるようになるはず。

しいていえばライブラリはどうしても慣れが必要で、Go言語系とはいえライブラリは結構設計が違ったりする。これはV言語も同じ。GitHub Copilotにアシストさせたりすると、Go言語の知識に引っ張られて間違えたりするので注意が必要。

あとはキラーとなるフレームワークやアプリケーション(Ruby on RailsとかUnityのようなもの)がOdinでも生まれれば、きっとどこかで爆発的にユーザは増えるのではないかなと感じる。これほど低レイヤーに触れてGCもないにもかかわらず、スクリプト言語のように書けて、Rustのような難しさもなく、Zigのような煩雑さもないというのは、設計が優れているということなのだろうなと思う。

しいていえば、C/C++でいうmallocやnewが、たとえ構造体等に包まれていてもすぐわかるようになっていればいいなと思うけれど、コアライブラリがそうなっているように allocator: mem.Allocator = context.allocator を必ず引数にとるようにするなどの目印を設ける習慣を徹底していれば、きっと大丈夫だと思う。(この辺は正直Zigのほうがすべてが明示的でわかりやすい部分。このあたりのノウハウも今後きっと蓄積されていくはずだし、temp_allocator などを使って面倒なことには目を瞑りたい人も多いだろうから、ライブラリ設計者が注意すればいいことな気もする。)

次に作りたいもの

Odinを書いていると、昔VSTプラグインを作ろうとして挫折した、大学時代を思い出した。先日VSTはオープンソース化が発表されたし、Odinならきっと簡単に書けるんじゃないかと思いつつ、GUIライブラリでVST向きのものがゲーム系しかない気がするので、まずはそこをどうするかかな。(Audio Units 対応とかクロスプラットフォームを考えないなら、Winforms 使えばいい気もする。ぶっちゃけWin/Mac用しか書かないだろうから、OSごとに分岐するでもいいかもしれない。)

Footnotes

  1. https://www.reddit.com/r/odinlang/comments/1czzc1k/freeing_memory_allocation_stored_in_unions/ このRedditに構造体のヒープ領域解放について似たような話が書いてあるけれど、ぶっちゃけ面倒になったらアリーナ作ってまとめて消せばいいと書いてある。まとめの最後にも自分もそう書いてるけど、temp_allocatorがあるのはそういう動機だろうし、集中型メモリ管理のよさはこうした割り切りにあるのだろうな。


このエントリーをはてなブックマークに追加follow us in feedly